MySQL AND with OR Condition
Real filters often mix AND and OR: laptops or phones under ₹30,000. How MySQL groups these operators decides which rows you get, and one missing pair of parentheses is one of the most common bugs in SQL. This tutorial shows exactly how mixed conditions are evaluated.
1. Operator Precedence
| Priority | Operator |
|---|---|
| 1 (highest) | Comparisons: =, <, >, IN, LIKE, BETWEEN, IS NULL |
| 2 | NOT |
| 3 | AND |
| 4 | XOR |
| 5 (lowest) | OR |
Table: products (7 rows)
| id | name | category | brand | price | in_stock | rating |
|---|---|---|---|---|---|---|
| 1 | Zen Laptop 14 | Laptop | Zentek | 58000 | 1 | 4.4 |
| 2 | Zen Laptop 13 | Laptop | Zentek | 24000 | 0 | 3.9 |
| 3 | Nova Phone | Phone | Novo | 27000 | 1 | 4.1 |
| 4 | Nova Phone Pro | Phone | Novo | 48000 | 1 | 4.6 |
| 5 | Pixel Tab | Tablet | Pixa | 29000 | 1 | 4.0 |
| 6 | Budget Phone | Phone | Kiwi | 9000 | 0 | 3.6 |
| 7 | Edge Laptop | Laptop | Edge | 72000 | 1 | NULL |
2. The Classic Mistake
Goal: laptops or phones costing under ₹30,000. Written without parentheses:
SELECT name, category, price FROM products
WHERE category = 'Laptop' OR category = 'Phone' AND price < 30000;
Result: 5 rows
| name | category | price |
|---|---|---|
| Zen Laptop 14 | Laptop | 58000 |
| Zen Laptop 13 | Laptop | 24000 |
| Nova Phone | Phone | 27000 |
| Budget Phone | Phone | 9000 |
| Edge Laptop | Laptop | 72000 |
Zen Laptop 14 (₹58,000) and Edge Laptop (₹72,000) slipped in, because MySQL read it as Laptop OR (Phone AND < 30000). With parentheses:
SELECT name, category, price FROM products
WHERE (category = 'Laptop' OR category = 'Phone') AND price < 30000;
Result: 3 rows
| name | category | price |
|---|---|---|
| Zen Laptop 13 | Laptop | 24000 |
| Nova Phone | Phone | 27000 |
| Budget Phone | Phone | 9000 |
3. Evaluating Step by Step
Let's evaluate both versions for the Zen Laptop 14 row (category Laptop, price 58000):
| Step | Without parentheses | With parentheses |
|---|---|---|
| Comparisons | TRUE OR FALSE AND FALSE | (TRUE OR FALSE) AND FALSE |
| Group | TRUE OR (FALSE AND FALSE) | (TRUE) AND FALSE |
| Result | TRUE OR FALSE = TRUE | FALSE |
SELECT TRUE OR FALSE AND FALSE AS no_parens, (TRUE OR FALSE) AND FALSE AS with_parens;
Result: 1 row
| no_parens | with_parens |
|---|---|
| 1 | 0 |
4. Useful Patterns
Alternatives sharing a common filter:
SELECT name, brand, price FROM products
WHERE in_stock = TRUE
AND (brand = 'Novo' OR brand = 'Pixa');
Result: 3 rows
| name | brand | price |
|---|---|---|
| Nova Phone | Novo | 27000 |
| Nova Phone Pro | Novo | 48000 |
| Pixel Tab | Pixa | 29000 |
Different rules per category (OR of AND-groups):
SELECT name, category, price, rating
FROM products
WHERE (category = 'Laptop' AND price < 60000 AND rating >= 4)
OR (category = 'Phone' AND price < 30000)
OR (category = 'Tablet' AND in_stock = TRUE);
Result: 4 rows
| name | category | price | rating |
|---|---|---|---|
| Zen Laptop 14 | Laptop | 58000 | 4.4 |
| Nova Phone | Phone | 27000 | 4.1 |
| Pixel Tab | Tablet | 29000 | 4.0 |
| Budget Phone | Phone | 9000 | 3.6 |
Simplify repeated ORs on one column with IN:
SELECT name FROM products WHERE category IN ('Laptop', 'Phone') AND price < 30000;
Result: 3 rows
| name |
|---|
| Zen Laptop 13 |
| Nova Phone |
| Budget Phone |
5. NULLs in Mixed Conditions
Edge Laptop's rating is NULL. In A OR (B AND rating >= 4) the NULL only matters if A is false:
SELECT name, rating FROM products WHERE brand = 'Edge' OR (category = 'Laptop' AND rating >= 4);
Result: 2 rows
| name | rating |
|---|---|
| Zen Laptop 14 | 4.4 |
| Edge Laptop | NULL |
SELECT name, rating FROM products WHERE category = 'Laptop' AND (rating >= 4 OR price > 70000);
Result: 2 rows
| name | rating |
|---|---|
| Zen Laptop 14 | 4.4 |
| Edge Laptop | NULL |
Edge Laptop appears in both: the first through brand = 'Edge', the second through price > 70000 (TRUE OR NULL is TRUE).
6. Adding NOT
NOT binds tighter than AND and OR, so it applies only to the condition right after it unless you add parentheses:
SELECT name FROM products WHERE NOT category = 'Phone' AND price < 30000;
NOT applies to category only
| name |
|---|
| Zen Laptop 13 |
| Pixel Tab |
SELECT name FROM products WHERE NOT (category = 'Phone' AND price < 30000);
NOT applies to the whole group
| name |
|---|
| Zen Laptop 14 |
| Zen Laptop 13 |
| Nova Phone Pro |
| Pixel Tab |
| Edge Laptop |
7. Writing Readable Conditions
- Put each AND/OR group on its own line, with the operator at the start of the line.
- Use parentheses around every OR group.
- Replace long OR chains on one column with IN.
- If a WHERE clause grows very large, consider a lookup table or a CASE expression.
- Test edge cases: boundary prices, NULLs, and rows that should be excluded.
8. Practice Exercises
Exercise 1: In-stock products that are either rated 4.5+ or cost under ₹10,000.
SELECT name, price, rating FROM products
WHERE in_stock = TRUE AND (rating >= 4.5 OR price < 10000);
Result: 1 row
| name | price | rating |
|---|---|---|
| Nova Phone Pro | 48000 | 4.6 |
Exercise 2: Zentek laptops, or any phone that is out of stock.
SELECT name, brand, in_stock FROM products
WHERE (brand = 'Zentek' AND category = 'Laptop')
OR (category = 'Phone' AND in_stock = FALSE);
Result: 3 rows
| name | brand | in_stock |
|---|---|---|
| Zen Laptop 14 | Zentek | 1 |
| Zen Laptop 13 | Zentek | 0 |
| Budget Phone | Kiwi | 0 |
9. Summary
- Precedence: comparisons, then NOT, then AND, then OR.
A OR B AND CmeansA OR (B AND C).- Always use parentheses when mixing AND and OR.
- TRUE OR NULL is TRUE and FALSE AND NULL is FALSE; other NULL combinations stay NULL.