SQL HAVING vs WHERE Explained for Beginners (Examples)
SQL HAVING vs WHERE explained for beginners: learn when to filter rows and when to filter groups, with simple copy-paste examples and clear answers.
SQL HAVING vs WHERE explained for beginners: learn when to filter rows and when to filter groups, with simple copy-paste examples and clear answers.
What is the difference between HAVING and WHERE in SQL? Short answer: WHERE filters individual rows before they are grouped. HAVING filters groups after they are made. If you want to keep only the groups that pass a test, such as customers with more than two orders, you use HAVING.
This guide is for absolute beginners. We define every term, use one small table, and explain each query line by line. You should already know what SELECT and GROUP BY do, but we will recap them quickly.
A table is a grid of data with rows and columns. Here is a table called orders. Each row is one order placed in an online shop.
CREATE TABLE orders (
id INT,
customer VARCHAR(50),
country VARCHAR(50),
amount INT
);
INSERT INTO orders (id, customer, country, amount) VALUES
(1, 'Ana', 'Spain', 40),
(2, 'Ben', 'Canada', 15),
(3, 'Ana', 'Spain', 60),
(4, 'Chen', 'China', 25),
(5, 'Ben', 'Canada', 30),
(6, 'Ana', 'Spain', 20),
(7, 'Dara', 'Kenya', 90);The first statement creates the table with four columns. The second adds seven rows. The amount column is the price of each order in dollars. You can run this in any SQL tool, such as PostgreSQL, MySQL, or an online SQL playground.
A row is one line of data. The WHERE clause is a condition that decides which rows to keep. Rows that do not match are thrown away.
SELECT *
FROM orders
WHERE amount > 30;SELECT * means show every column. FROM orders says which table to read. WHERE amount > 30 keeps only rows where amount is bigger than 30. The result is three rows: orders 1, 3 and 7. WHERE looks at one row at a time.
GROUP BY puts rows that share the same value into one group. You then use an aggregate function to calculate one number per group. An aggregate function is a function that works on many rows and returns one value. Common ones are COUNT, SUM and AVG.
SELECT customer, COUNT(*) AS order_count, SUM(amount) AS total
FROM orders
GROUP BY customer;GROUP BY customer makes one group for each person. COUNT(*) counts the rows in each group. SUM(amount) adds up the amounts. AS gives a column a friendly name. The result has one row per customer: Ana has 3 orders and a total of 120, Ben has 2 and 45, Chen has 1 and 25, and Dara has 1 and 90.
Now suppose you want only customers with more than one order. You might try this:
SELECT customer, COUNT(*) AS order_count
FROM orders
WHERE COUNT(*) > 1
GROUP BY customer;This gives an error. SQL runs WHERE before it makes groups, so at that moment there is no count yet. WHERE cannot use aggregate functions. This is the exact problem HAVING solves.
HAVING is a condition that runs after GROUP BY. It can use aggregate functions like COUNT and SUM.
SELECT customer, COUNT(*) AS order_count
FROM orders
GROUP BY customer
HAVING COUNT(*) > 1;First, GROUP BY customer makes the groups. Then HAVING COUNT(*) > 1 keeps only groups with more than one row. The result is Ana with 3 orders and Ben with 2. Chen and Dara are removed because they have only one order each.
| Feature | WHERE | HAVING |
|---|---|---|
| What it filters | Individual rows | Groups of rows |
| When it runs | Before GROUP BY | After GROUP BY |
| Can use COUNT, SUM, AVG? | No | Yes |
| Needs GROUP BY? | No | Usually yes |
| Example | WHERE amount > 30 | HAVING SUM(amount) > 100 |
You can use both in one query. WHERE cleans up the rows first. HAVING then filters the groups that are left.
SELECT customer, SUM(amount) AS total
FROM orders
WHERE amount >= 25
GROUP BY customer
HAVING SUM(amount) > 50
ORDER BY total DESC;Step by step: WHERE amount >= 25 removes small orders (orders 2 and 6, worth 15 and 20, are removed; order 4, worth exactly 25, stays). GROUP BY customer groups the remaining rows. HAVING SUM(amount) > 50 keeps customers whose total is above 50. ORDER BY total DESC sorts from biggest to smallest. Dara (90) and Ana (100) pass, so Ana comes first.
You write SELECT first, but SQL thinks about the query in a different order. Knowing this makes HAVING and WHERE easy to remember.
-- Better: filter rows with WHERE, not HAVING
SELECT country, COUNT(*)
FROM orders
WHERE country = 'Spain'
GROUP BY country;Here the test is on the plain column country, so WHERE is the right tool. It removes rows early, so SQL has less work to do when it groups.
New to grouping? Read our beginner guide to GROUP BY first, then come back to practice HAVING.
Read: SQL GROUP BY explainedWant to know more about COUNT, SUM and AVG? Our aggregate functions guide has simple examples.
Read: SQL aggregate functionsWHERE filters individual rows before grouping. HAVING filters groups after GROUP BY has run. Only HAVING can use aggregate functions such as COUNT, SUM and AVG.
Yes, in most databases. SQL then treats the whole table as one group. It is rare, though, and beginners almost always use HAVING together with GROUP BY.
Yes. WHERE runs first and removes rows, then GROUP BY makes groups, then HAVING removes groups. Using both is very common in real reports.
SQL runs WHERE before groups exist, so there is no count to compare yet. Move the test into a HAVING clause after GROUP BY.
Often yes, because HAVING works after grouping. If a condition only needs a normal column, put it in WHERE so fewer rows get grouped.
Learning to code or need a database-backed website built? Our team can help.
Talk to FepiqOccasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.
Keep reading
Learn SQL CASE WHEN step by step. This beginner guide shows how to label, group and sort data with simple examples you can copy and run today.
PostgreSQL ILIKE explained for beginners: search text without worrying about case, learn % and _ wildcards, and copy simple examples you can run today.
Let's build something
Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.