Does AND bind tighter than OR in a WHERE clause, and what bug does that cause?
answer
- one connective binds tighter than the other
- think arithmetic: times before plus
- AND behaves like multiplication
- order is NOT, then AND, then OR
- parenthesize the OR group
basics
~20 sYes. Standard SQL precedence is NOT, then AND, then OR, so a OR b AND c means a OR (b AND c). The classic bug is an OR list of values silently escaping a filter that was meant to apply to all of them.
solid answer
~50 sSQL's logical operators have fixed precedence: comparisons evaluate first, then `NOT`, then `AND`, then `OR`. So `WHERE plan = 'free' OR plan = 'pro' AND status = 'active'` parses as `plan = 'free' OR (plan = 'pro' AND status = 'active')` — every free-plan row comes back regardless of status, which is almost never what the author meant. The fix is parentheses around the OR group: `WHERE (plan = 'free' OR plan = 'pro') AND status = 'active'`. Because the bug returns a plausible-looking superset rather than an error, it survives code review and shows up as "the report has extra rows". The habit that prevents it is to parenthesize any OR group the moment an AND appears in the same clause, and to write repeated equality ORs on one column as an `IN` list instead.
code
sql · 10 lines-- BUG: parses as plan = 'free' OR (plan = 'pro' AND status = 'active')
SELECT subscription_id, plan, status
FROM subscriptions
WHERE plan = 'free' OR plan = 'pro' AND status = 'active';
-- FIX: parenthesize the OR group
SELECT subscription_id, plan, status
FROM subscriptions
WHERE (plan = 'free' OR plan = 'pro')
AND status = 'active';go deeper
Memorize the order NOT, then AND, then OR, and be able to insert the implied parentheses into a clause on the spot. Expect to be handed a mixed AND/OR predicate and asked which rows it really matches.
Explain the parse rather than the symptom: rewrite the buggy clause with explicit parentheses, show the corrected version, and note that an equality OR chain on one column is better expressed as an IN list.
Frame it as a silent-wrong-rows defect: no error, a superset of the intended result, discovered from business impact. Talk about the review habits and formatting conventions that keep the bug out of shared queries and generated filters.
Own the guardrails: predicate-building helpers that parenthesize by construction, review checklists that flag mixed AND/OR without parentheses, and tests that assert a scoped query returns nothing outside its scope rather than merely returning rows.
## The precedence ladder A `WHERE` clause is one boolean expression built from predicates and the connectives `NOT`, `AND`, `OR`. SQL fixes how that expression is parsed: 1. Comparison predicates (`=`, `<>`, `<`, `>`, `<=`, `>=`) and the other predicates bind first — they produce the truth values. 2. `NOT` binds next, applying to the single predicate or parenthesized group immediately to its right. 3. `AND` binds next. 4. `OR` binds last (loosest). So `a OR b AND c` is `a OR (b AND c)`, and `NOT a AND b` is `(NOT a) AND b`. Parentheses override all of it, and they are the only thing that does. ## The bug this creates The overwhelmingly common mistake is an OR list of alternative values written next to an AND filter: ```sql SELECT * FROM subscriptions WHERE plan = 'free' OR plan = 'pro' AND status = 'active'; ``` The author read this left to right as "plan is free or pro, and the subscription is active". The parser read it as: ```sql WHERE plan = 'free' OR (plan = 'pro' AND status = 'active') ``` Every free-plan row is returned — cancelled, expired, anything — because the `status` test never applies to that branch. Nothing errors. The result set is a superset of the intended one, so it looks fine on a small dev dataset and quietly inflates a report, a billing run or a notification batch in production. The same shape appears with date filters (`WHERE region = 'EU' OR region = 'UK' AND created_at >= DATE '2026-01-01'`) and with tenant or ownership scoping, where the consequence is that rows belonging to other tenants leak into a scoped query. ## Reading a predicate the way the parser does A quick mental method: treat `AND` as multiplication and `OR` as addition, then insert the implied parentheses exactly where you would in arithmetic. `a + b * c` is `a + (b * c)`; `a OR b AND c` is `a OR (b AND c)`. If the reading you get differs from the sentence you had in your head, the clause needs parentheses. ## Two fixes Parenthesize the OR group: ```sql WHERE (plan = 'free' OR plan = 'pro') AND status = 'active'; ``` Or collapse the repeated equality tests on one column into an `IN` list, which removes the OR entirely and is usually easier to read: ```sql WHERE plan IN ('free', 'pro') AND status = 'active'; ``` Both mean the same thing. The second one cannot be broken by a later edit that appends another `AND`, which is why it is the better default when the OR branches are alternative values of a single column. ## NOT is part of the same trap `NOT` binds tighter than `AND` and `OR`, so `WHERE NOT status = 'active' OR priority = 1` negates only the first comparison; it is `(NOT status = 'active') OR priority = 1`. If you meant to negate the whole disjunction, you must write `NOT (status = 'active' OR priority = 1)`. ## Meaning versus evaluation Parentheses determine what the expression *means*. They say nothing about the order in which an engine actually tests the operands — a query engine is free to evaluate a cheap predicate before an expensive one, or to skip evaluation once the outcome is fixed, as long as the answer matches the defined meaning. Never write a predicate that depends on left-to-right short-circuiting for correctness (for example, a division guard placed "first" in an `AND`); use a `CASE` expression if you truly need ordered evaluation. ## Style rules that prevent it - Parenthesize an OR group the moment an `AND` appears anywhere in the same clause, even when precedence would have given you the right answer. It costs two characters and it survives the next edit. - Put each top-level `AND` condition on its own line, with the operator leading the line. A stray OR then stands out visually. - Prefer `IN (...)` over a chain of ORs on the same column. - When a query returns more rows than expected, check the connectives before you suspect the join or the data. ## What interviewers listen for They want the precedence order stated plainly (NOT, AND, OR), the rewritten parse of the buggy clause, and the observation that the failure mode is *silent wrong rows*, not an error. Candidates who say "SQL evaluates left to right" have the wrong model and will reproduce the bug.
- Where does NOT sit in that precedence order, and what does WHERE NOT a = 1 OR b = 2 actually mean?`NOT` binds tighter than both `AND` and `OR`, so it applies only to the predicate immediately after it. `WHERE NOT a = 1 OR b = 2` means `(NOT a = 1) OR b = 2`. To negate the whole disjunction you must write `NOT (a = 1 OR b = 2)`, which by De Morgan equals `a <> 1 AND b <> 2` for non-null columns.
- If parentheses fix the parse, do they also fix the order in which the engine tests the conditions?No. Parentheses define the meaning of the expression, not the runtime evaluation order. An engine may test a cheap predicate first, or stop once the result is determined, provided the final answer matches the defined semantics. Never rely on left-to-right short-circuiting for correctness; if you need ordered evaluation, express it with a `CASE` expression.
- Why is this bug so often caught late rather than at development time?It produces a superset of the intended rows instead of an error. On a small development dataset the extra rows may not exist at all, and in production the query still runs and still returns plausible data. It usually surfaces as a business complaint — a report with too many rows, or a batch that touched records it should have skipped.
AND is SQL's multiplication and OR is its addition: just as 2 + 3 * 4 is 2 + (3 * 4), the clause a OR b AND c is a OR (b AND c).
saying these in an interview costs you the question
- Claims SQL evaluates WHERE conditions strictly left to right
- Says OR binds tighter than AND
- Thinks the mis-parenthesized query raises an error rather than returning extra rows
- Believes parentheses control the engine's runtime evaluation order
- Adds a new AND condition to an OR chain without re-checking grouping