Most of the time you don't want every row. You want the customers in Leeds, the products under 10 pounds, or the orders from last week. The WHERE clause filters a table so the result contains only the rows that match a condition.
WHERE comes after FROM. For every row, the database checks the condition. If it's true, the row is included; if not, it's skipped. Conditions can compare numbers, text and dates, combine several tests, and match patterns in text.
In this lesson you'll learn the comparison operators, combine conditions with AND, OR and NOT, and use the shortcuts IN, BETWEEN, LIKE and IS NULL.
Comparison operators
| Operator | Meaning | Example |
|---|---|---|
= | equal to | city = 'Leeds' |
<> or != | not equal to | category <> 'Bags' |
> < | greater / less than | price > 10 |
>= <= | greater or equal / less or equal | stock <= 20 |
Notice that SQL uses a single = for comparing. Text values go in single quotes: 'Leeds'. Numbers don't need quotes. Dates are usually written as text in the form '2024-03-15' and compare in date order.
AND, OR and NOT
ANDkeeps a row only if both conditions are true.ORkeeps a row if either condition is true.NOTreverses a condition.
SELECT name, price
FROM products
WHERE category = 'Kitchen' AND price < 10;When you mix AND and OR, AND is checked first, just as multiplication comes before addition in maths. Use brackets to make your meaning clear: WHERE (city = 'Leeds' OR city = 'Lyon') AND joined >= '2024-01-01'.
IN and BETWEEN
IN checks against a list of values. It's a tidy replacement for several ORs:
WHERE city IN ('Leeds', 'Lisbon', 'Porto')BETWEEN checks a range, including both ends:
WHERE price BETWEEN 5 AND 25Both work with numbers, text and dates, and both can be reversed with NOT: NOT IN (...), NOT BETWEEN ....
LIKE and IS NULL
LIKE matches text against a pattern:
%stands for any number of characters, including none._stands for exactly one character.
So name LIKE 'D%' finds names starting with D, name LIKE '%box' finds names ending in "box", and name LIKE '_en' matches "Ben" and "Pen".
A NULL value means "unknown" or "missing". You can't test it with =. Use IS NULL or IS NOT NULL instead: WHERE city IS NULL.
Filtering by dates
Dates stored as DATE or in ISO text format, YYYY-MM-DD, compare in calendar order, so ordinary comparison operators work:
SELECT id, order_date
FROM orders
WHERE order_date >= '2024-03-01'
AND order_date < '2024-04-01';This half-open range, from the first day of March up to but not including the first of April, is the safest way to select a whole month. It works for dates and for timestamps with times of day. BETWEEN '2024-03-01' AND '2024-03-31' looks equivalent, but with timestamps it misses everything after midnight on 31 March. The dates and times lesson covers date functions.
Upper and lower case in text filters
Whether city = 'leeds' matches Leeds depends on the database. PostgreSQL and SQLite compare text exactly, so case matters; MySQL's default settings ignore case. LIKE follows similar rules, except that SQLite's LIKE ignores case for English letters, and PostgreSQL offers ILIKE for case-insensitive patterns. To match regardless of case anywhere, compare lowercase versions: WHERE LOWER(city) = 'leeds'.
SQL WHERE Syntax
WHERE
SELECT columns
FROM table_name
WHERE condition;
-- examples of conditions
WHERE price >= 10 AND stock > 0
WHERE city IN ('Leeds', 'Lyon')
WHERE joined BETWEEN '2024-01-01' AND '2024-12-31'
WHERE name LIKE 'A%'
WHERE city IS NULLSQL WHERE Summary
WHERE conditionkeeps only the rows where the condition is true.- Compare with
=,<>,>,<,>=and<=. Text goes in single quotes. - Combine tests with
AND,ORandNOT; use brackets to group them. IN (...)checks a list;BETWEEN a AND bchecks an inclusive range.LIKEmatches patterns with%and_; test missing values withIS NULL.
SQL WHERE Examples
Each example shows the code, the output it produces and what each part does.
Customers in one city
SELECT name, joined
FROM customers
WHERE city = 'Leeds';name | joined
-----+-----------
Ben | 2023-05-22
Dev | 2024-03-15Line-by-line explanation
- Line 3Only rows where city is exactly 'Leeds' are returned.
Cheap products in stock
SELECT name, price, stock
FROM products
WHERE price < 15 AND stock > 0;name | price | stock
-------------+-------+------
Notebook | 3.50 | 120
Pen | 1.20 | 300
Water bottle | 12.00 | 40Line-by-line explanation
- Line 3Both tests must pass. The lunch box is cheap but has 0 in stock, so it's left out.
Ranges, lists and patterns
SELECT name, category, price
FROM products
WHERE price BETWEEN 5 AND 30
AND category IN ('Kitchen', 'Home');name | category | price
-------------+----------+------
Water bottle | Kitchen | 12.00
Lunch box | Kitchen | 8.75
Desk lamp | Home | 24.50Line-by-line explanation
- Line 3BETWEEN includes 5 and 30 themselves.
- Line 4IN keeps rows whose category is in the list.
Names that match a pattern
SELECT name
FROM products
WHERE name LIKE '%o%';name
------------
Notebook
Water bottle
Lunch boxLine-by-line explanation
- Line 3% on both sides means "contains an o anywhere". In SQLite and MySQL, LIKE ignores letter case for English text; in PostgreSQL use ILIKE for that.
Common SQL WHERE Mistakes
Double quotes around text
SELECT * FROM customers WHERE city = "Leeds";SELECT * FROM customers WHERE city = 'Leeds';Testing NULL with =
SELECT * FROM customers WHERE city = NULL;SELECT * FROM customers WHERE city IS NULL;Nothing is ever equal to NULL, not even another NULL, so the first query always returns zero rows.
Mixing AND and OR without brackets
SELECT name FROM customers
WHERE city = 'Leeds' OR city = 'Lyon' AND joined >= '2024-01-01';SELECT name FROM customers
WHERE (city = 'Leeds' OR city = 'Lyon') AND joined >= '2024-01-01';Without brackets, AND is applied first, so Ben from Leeds is returned even though he joined in 2023.
SQL WHERE in Real Projects
Filtering in real apps
Every search box and filter on a website ends up as a WHERE clause. Choosing "Kitchen" and "under 20" in a shop's sidebar becomes WHERE category = 'Kitchen' AND price < 20, and typing in a search box often becomes a LIKE '%word%' condition.
SQL WHERE FAQ
Can I filter on COUNT or SUM in WHERE?
No. WHERE filters individual rows before any grouping happens, so aggregate results don't exist yet. Use HAVING after GROUP BY, for example HAVING COUNT(*) > 2, as the ORDER BY and GROUP BY lesson shows.
How do I search for a literal % or _ with LIKE?
Escape it and declare the escape character: WHERE note LIKE '%50\%%' ESCAPE '\' finds text containing "50%". Without escaping, % means "any characters" and _ means "any one character", so a search for 50% would match far more than intended.
Is IN faster than several OR conditions?
They usually perform the same, because the database treats city IN ('Leeds', 'Lyon') and city = 'Leeds' OR city = 'Lyon' the same way. Use IN because it's shorter and avoids mistakes with brackets when combined with AND.
Why does NOT IN sometimes return no rows?
If the list or subquery contains a NULL, x NOT IN (1, NULL) can never be true: the comparison with NULL is unknown, so every row is filtered out. Remove the NULLs with WHERE col IS NOT NULL in the subquery, or use NOT EXISTS. The NULL values lesson explains why.
Does the order of conditions in WHERE affect speed?
Generally not: the database's query planner decides which conditions to check first and whether to use an index. The order does matter for meaning when you mix AND and OR, so use brackets to make the logic explicit.
SQL WHERE Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the query to find products that cost more than 20.
SELECT name, price
FROM products
price 20;
Which names does this return?
SELECT name FROM customers
WHERE city IN ('Lisbon', 'Porto');
Which pattern finds product names that end in "lamp"?
How many products does this return?
SELECT name FROM products
WHERE stock BETWEEN 15 AND 40;
Which condition finds products whose name starts with "B"?
SQL WHERE Quiz
5 questions to check what you learned.
Which clause filters rows in a SELECT query?
- FILTER
- WHERE
- FROM
- ORDER BY
How should you write a text value in a condition?
- Leeds
- (Leeds)
- {Leeds}
- 'Leeds'
How do you find rows where
cityhas no value?- WHERE city = NULL
- WHERE city = ''
- WHERE city IS NULL
- WHERE NULL(city)
Is
price BETWEEN 10 AND 20the same asprice >= 10 AND price <= 20?- No, BETWEEN excludes both ends
- No, BETWEEN excludes 20
- Only for whole numbers
- Yes, BETWEEN includes both ends
Which rows does
WHERE price BETWEEN 5 AND 10include?- Prices above 5 and below 10 only
- Prices from 5 to 10, including 5 and 10
- Only prices of exactly 5 or 10
- Prices below 5 or above 10