WHERE
Filter rows before grouping, and understand how NULL and join filters change the result.
Syntax
SELECT ...
FROM table
WHERE predicate [AND | OR predicate ...];What it does
WHERE removes rows before GROUP BY, aggregates, and window functions run. A row is kept only when the predicate evaluates to TRUE — a predicate that evaluates to FALSE or UNKNOWN drops the row.
Example
SELECT
customer_id,
amount
FROM orders
WHERE status = 'paid'
AND amount > 0;NULL makes predicates UNKNOWN
WHERE cancelled_at = NULL -- never matches
WHERE cancelled_at IS NULL -- correctAny comparison with NULL returns UNKNOWN, so WHERE col <> 'x' silently drops rows where col is NULL. Add OR col IS NULL when those rows should stay.
LEFT JOIN filters belong in ON
-- turns the LEFT JOIN into an INNER JOIN
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE o.status = 'paid';
-- keeps unmatched customers
FROM customers c
LEFT JOIN orders o
ON c.customer_id = o.customer_id
AND o.status = 'paid';A predicate on the right-side table in WHERE removes the NULL-filled unmatched rows, collapsing the outer join. Move the condition into ON when you need to keep them.
Safety check before shipping
- decide whether
NULLrows should pass or be excluded for each predicate - confirm right-side filters on outer joins are in
ON, notWHERE, unless the collapse is intended - check row counts before and after adding a filter