SQL Quest

Lessons, hints, explained solutions, the in-browser PostgreSQL editor and practice answer checks in your browser are available. Graded assessments, recorded progress from answer checks and new certificates aren't available yet.

WHERE

Keeps only the rows for which a condition is TRUE.

Syntax

SELECT ... FROM table
WHERE condition [AND | OR condition ...];

WHERE is evaluated once per row, before grouping. A row is kept only when the condition is TRUE; rows where it is FALSE or NULL (unknown) are dropped.

  • Combine conditions with AND, OR and NOT. AND binds tighter than OR, so a OR b AND c means a OR (b AND c). Use parentheses whenever you mix them.
  • Compare text with single-quoted literals: city = 'Riyadh'. Text comparison is case-sensitive.
  • Dates can be compared with ISO literals: order_date >= '2025-03-01'.
  • WHERE cannot use aggregates (SUM, COUNT) — use HAVING — and cannot use window functions or select-list aliases, because it runs before those are computed.
  • NULL: col = NULL is never true. Use col IS NULL. And col <> 'x' silently drops rows where col is NULL.

Examples

Text and number conditions starter
SELECT product_name, category, price
FROM products
WHERE category = 'Kitchen' AND price < 200
ORDER BY price;

Both conditions must be TRUE for a row to stay.

Mixing AND and OR safely starter
SELECT order_id, status, quantity
FROM orders
WHERE (status = 'shipped' OR status = 'processing')
  AND quantity >= 2
ORDER BY order_id;

Without the parentheses, quantity >= 2 would apply only to processing orders.

Date range on a timestamp shop
SELECT order_id, ordered_at, status
FROM orders
WHERE ordered_at >= '2025-03-01' AND ordered_at < '2025-04-01'
ORDER BY ordered_at
LIMIT 10;

A half-open range (>= start, < next start) captures the whole of March, including 31 March 23:59.

Pitfalls

  • Writing col = NULL instead of col IS NULL (it matches nothing).

  • Forgetting parentheses when mixing AND and OR.

  • Filtering a timestamp with BETWEEN '2025-03-01' AND '2025-03-31': it stops at 31 March 00:00:00 and misses the rest of that day.

  • Using a select-list alias in WHERE (column "alias" does not exist): repeat the expression or wrap the query in a subquery/CTE.

  • Putting an aggregate in WHERE (aggregate functions are not allowed in WHERE): filter groups with HAVING.