All posts
SQLSeptember 28, 20267 min read

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.

F
Fepiq Team
Fepiq

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.

The example table we will use

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.

sql
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.

Quick recap: WHERE filters rows

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.

sql
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.

Quick recap: GROUP BY makes groups

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.

sql
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.

Why WHERE cannot filter groups

Now suppose you want only customers with more than one order. You might try this:

sql
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 filters groups

HAVING is a condition that runs after GROUP BY. It can use aggregate functions like COUNT and SUM.

sql
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.

HAVING vs WHERE: side-by-side

FeatureWHEREHAVING
What it filtersIndividual rowsGroups of rows
When it runsBefore GROUP BYAfter GROUP BY
Can use COUNT, SUM, AVG?NoYes
Needs GROUP BY?NoUsually yes
ExampleWHERE amount > 30HAVING SUM(amount) > 100

Using WHERE and HAVING together

You can use both in one query. WHERE cleans up the rows first. HAVING then filters the groups that are left.

sql
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.

The order SQL runs your query

You write SELECT first, but SQL thinks about the query in a different order. Knowing this makes HAVING and WHERE easy to remember.

  1. FROM: pick the table.
  2. WHERE: filter rows.
  3. GROUP BY: make groups.
  4. HAVING: filter groups.
  5. SELECT: choose the columns to show.
  6. ORDER BY: sort the result.

Common beginner mistakes

  • Using WHERE with COUNT or SUM. Use HAVING instead.
  • Using HAVING for a simple row filter. If the test is on a normal column such as country, WHERE is clearer and usually faster.
  • Writing HAVING before GROUP BY. The correct order is WHERE, GROUP BY, HAVING, ORDER BY.
  • Forgetting that a column in SELECT must be in GROUP BY or inside an aggregate function.
sql
-- 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.

Practice ideas

  • Find countries with a total amount above 40.
  • Find customers whose average order is above 30 (use AVG).
  • Find customers with at least 2 orders from orders over 20 dollars (WHERE plus HAVING).

New to grouping? Read our beginner guide to GROUP BY first, then come back to practice HAVING.

Read: SQL GROUP BY explained

Want to know more about COUNT, SUM and AVG? Our aggregate functions guide has simple examples.

Read: SQL aggregate functions

Frequently asked questions

What is the difference between WHERE and HAVING in SQL?+

WHERE 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.

Can I use HAVING without GROUP BY?+

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.

Can I use WHERE and HAVING in the same query?+

Yes. WHERE runs first and removes rows, then GROUP BY makes groups, then HAVING removes groups. Using both is very common in real reports.

Why does WHERE COUNT(*) > 1 give an error?+

SQL runs WHERE before groups exist, so there is no count to compare yet. Move the test into a HAVING clause after GROUP BY.

Is HAVING slower than WHERE?+

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 Fepiq

Get new posts in your inbox

Occasional, no-fluff notes on shipping modern software — startups, automation, Laravel, Shopify and more. No spam, unsubscribe anytime.

Keep reading

Related posts

All posts

Let's build something

Ready to ship your next product with Fepiq?

Book a free discovery call. We'll listen, ask sharp questions, and send you a proposal within 3 business days.