AND, OR, NOT and Operator Precedence
Combining conditions correctly. Precedence between AND and OR, what NOT does to a NULL, and how to keep complex filters readable and index friendly.
- SQL Basics
- DDL
- DML
- SELECT
- WHERE
- Functions
- NULL and Logic
- Aggregate Functions
- GROUP BY
- JOIN
- Subqueries
- Set Operations
- CTEs
- Constraints
- Keys
- Relationships
- Database Design
- Normalisation
- Views
- Window Functions
- Advanced SQL
- Procedures and Functions
- Triggers
- Temporary Tables
- Transactions
- Isolation and Locking
- Indexes
- Query Performance
- SQL Security
- SQL Dialects
- Practical SQL
Concept
Logical operators join conditions into one expression. There are three, and they have a strict precedence order that does not match how English sentences are read.
| Precedence | Operator | True when |
|---|---|---|
| 1 (highest) | NOT | the condition is FALSE |
| 2 | AND | both sides are TRUE |
| 3 (lowest) | OR | either side is TRUE |
Syntax
WHERE condition1 AND condition2
WHERE condition1 OR condition2
WHERE NOT condition
WHERE (condition1 OR condition2) AND condition3Example
-- What people usually mean:
-- employees in department 10 or 20, earning over 80000
SELECT first_name, dept_id, salary
FROM employees
WHERE (dept_id = 10 OR dept_id = 20)
AND salary > 80000;
-- What they often write, which means something else entirely
SELECT first_name, dept_id, salary
FROM employees
WHERE dept_id = 10 OR dept_id = 20 AND salary > 80000;Explanation
Because AND binds tighter than OR, the second query is read as dept_id = 10 OR (dept_id = 20 AND salary > 80000). Every employee in department 10 is returned regardless of salary. The query does not error - it quietly answers a different question, which is far worse.
NOT and NULL
-- These two are NOT equivalent when status can be NULL
SELECT * FROM employees WHERE NOT (status = 'active');
SELECT * FROM employees WHERE status <> 'active' OR status IS NULL;If status is NULL, status = 'active' is UNKNOWN, and NOT UNKNOWN is still UNKNOWN - so the row is filtered out. NOT cannot rescue a NULL. Only IS NULL can.
Short circuit is not guaranteed
-- Do not rely on left to right evaluation for safety
SELECT * FROM orders
WHERE total <> 0 AND (1000 / total) > 5; -- may still divide by zeroUnlike most programming languages, SQL does not promise to evaluate AND operands in order. The optimiser may reorder conditions freely. Use CASE or NULLIF when an operation must be guarded.
Important rules
- Precedence is
NOT, thenAND, thenOR. Parentheses override it. NOT UNKNOWNisUNKNOWN, soNOTnever converts aNULLrow into a match.ORacross different columns often prevents efficient index use; the engine may switch to a full scan or an index merge.WHERE a = 1 OR a = 2 OR a = 3is exactlyWHERE a IN (1, 2, 3), and the latter is easier to read.
Common mistakes
- Mixing
ANDandORwithout parentheses - by far the most common logic bug in SQL. - Expecting
NOT INto work when the list can containNULL; it returns no rows at all. See the NULL notes. - Assuming a guard condition placed first will protect a later division.
- Writing
WHERE NOT status = 'active'and losing everyNULLrow silently.
Best practices
- Parenthesise every mixed
AND/ORexpression, even when the default precedence is already correct. The next reader should not have to remember the table. - Put each condition on its own line and align the operators; complex filters become reviewable.
- Replace long
ORchains on one column withIN. - Handle
NULLexplicitly rather than hopingNOTcovers it.
Practice
- Write a filter for active employees in Engineering or Finance hired after 2020.
- Explain what
WHERE NOT (dept_id = 10 AND salary > 80000)returns for an employee withdept_id = NULL. - Rewrite
WHERE status = 'pending' OR status = 'shipped' OR status = 'packed'more concisely.