Getting output is not the same as getting it right.
Interviewers look for the traps: NULLs, joins that multiply rows, ties, integer division, date ranges.
Check your next query against these 8 before you say "done".
Checks 1-4: NULLs and joins that lie
- 01
COUNT(*) vs COUNT(col)
COUNT(phone) skips NULLs. Know which one the question wants.
SELECT COUNT(*) AS all_rows, COUNT(phone) AS has_phone FROM users; - 02
= NULL never matches
Comparing with NULL gives unknown, so the row is dropped. Use IS NULL.
SELECT name FROM employees WHERE manager_id IS NULL; - 03
LEFT JOIN + WHERE
A filter on the right table in WHERE turns it into an inner join. Put it in ON.
FROM users u LEFT JOIN orders o ON o.user_id = u.id AND o.status = 'Paid' - 04
Rows that multiply
One user, 5 orders, 3 addresses = 15 rows. Count rows before and after every join.
SELECT COUNT(*) FROM orders; -- run again after the JOIN
Checks 5-8: ties, maths, dates, groups
- 05
Ties in ranking
2nd highest salary with two people on top? DENSE_RANK keeps ties together.
DENSE_RANK() OVER ( ORDER BY salary DESC) AS rnk - 06
Integer division
In SQL Server and PostgreSQL, 7/2 gives 3 on whole numbers. Multiply by 100.0 first.
SELECT paid * 100.0 / total AS paid_pct FROM summary; - 07
Date ranges
BETWEEN on a timestamp misses the last day after midnight. Use >= and <.
WHERE order_ts >= '2026-09-01' AND order_ts < '2026-10-01' - 08
WHERE vs HAVING
WHERE filters rows before grouping. HAVING filters the groups.
GROUP BY city HAVING COUNT(*) > 10;
How you talk matters too
Loses marks
- Starts typing before asking anything
- SELECT * everywhere
- Says "done" without looking at the output
Gets the offer
- Repeats the question: one row per what?
- Asks about ties and NULLs
- Checks the row count before saying done
Test yourself live
Try the traps on real tables: customers who never ordered, highest paid with ties, cancellation %. Free, checked instantly.