Combining Predicates: AND / OR / NOT
Compose multiple conditions with correct precedence and parentheses.
A misplaced paren silently ships the wrong rows
A WHERE clause is where a business rule becomes code. "Paid or shipped orders in the EU" is one English sentence, but SQL does not read English. It applies a fixed precedence order, and if that order does not match what you meant, the query still runs, returns no error, and hands wrong rows to whatever reads the table next: a dashboard, a payout job, a training set. Filter bugs are quiet, and quiet bugs are the expensive ones.
Precedence: NOT, then AND, then OR
SQL evaluates the logical operators in a fixed order: NOT binds tightest, then AND, then OR. AND relates to OR the way * relates to +. So this filter:
WHERE status = 'paid' OR status = 'shipped' AND region = 'EU'
does not mean "(paid or shipped) and in the EU." SQL groups the tighter AND first and reads it as:
WHERE status = 'paid' OR (status = 'shipped' AND region = 'EU')
The paid branch now stands alone with no region test, so every paid order from every region slips through. Against the seed data, order 3 (paid, US) comes back even though it is not in the EU. The filter silently widened.
The fix: parenthesize the OR group
WHERE (status = 'paid' OR status = 'shipped')
AND region = 'EU';
Now the alternatives are grouped before AND applies, so both branches must also satisfy region = 'EU'. This returns exactly orders 1 and 2. Parentheses override precedence, and they document intent, so a reviewer never has to recall the precedence table to trust the clause.
When a rule stacks two independent choices, give each choice its own parenthesized group:
WHERE (status = 'paid' OR status = 'shipped')
AND (region = 'EU' OR region = 'UK')
AND account_type <> 'test';
Each OR lives inside its own parentheses, and the ANDs chain the independent conditions together.
Pitfall: NOT is tighter than you think
NOT binds tighter than AND, so NOT status = 'paid' AND region = 'EU' parses as (NOT status = 'paid') AND region = 'EU', not NOT (status = 'paid' AND region = 'EU'). When you mean to negate a whole group, wrap the group: NOT (status = 'paid' AND region = 'EU').
Interview nuance: SQL predicates are three-valued. Every condition evaluates to TRUE, FALSE, or UNKNOWN, and WHERE keeps only rows that come out TRUE. A NULL compared with anything yields UNKNOWN, and NOT UNKNOWN is still UNKNOWN. So account_type <> 'test' (equivalently NOT (account_type = 'test')) silently drops every row where account_type is NULL, even though those rows are not test accounts. To keep them, say so explicitly: (account_type <> 'test' OR account_type IS NULL). Interviewers use this to check whether you actually understand three-valued logic instead of treating NULL like an ordinary value.
CREATE TABLE orders (
order_id INTEGER,
status TEXT,
region TEXT
);
INSERT INTO orders VALUES
(1, 'paid', 'EU'),
(2, 'shipped', 'EU'),
(3, 'paid', 'US'),
(4, 'cancelled', 'EU'),
(5, 'shipped', 'UK');SELECT order_id, status, region
FROM orders
WHERE (status = 'paid' OR status = 'shipped')
AND region = 'EU';Apply
Your turn
The task this lesson builds to.
Return order_id, status, region for orders that are (status = 'paid' or status = 'shipped') and region = 'EU'. Get the grouping right.
3 hints and 1 automated check are waiting in the workspace.
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Reproduce this business rule verbatim: "orders that are paid OR shipped, in region EU or UK, excluding test accounts." Return order_id, status, region. The grouping must be exact.
3 hints and 1 automated check are waiting in the workspace.