← Back to MySQL Course | Chapter 10: Aggregate Functions & Grouping | Lesson 7 of 7

HAVING Clause

HAVING is like a teacher checking which piles are big enough after you have sorted them. It filters groups, not single rows.
Syntax
sql
SELECT column_name, aggregate_function(column)
FROM table_name
GROUP BY column_name
HAVING aggregate_condition;

Filtering Groups

The HAVING clause filters grouped data after aggregation has already happened. Because the WHERE clause cannot evaluate aggregate functions like COUNT() or SUM(), you must use HAVING to filter groups based on those aggregate results.

Example: Filtering Groups

sql
CREATE TABLE orders (id INT, customer_id INT);
INSERT INTO orders VALUES (1, 1), (2, 1), (3, 2);
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > 1;

HAVING vs WHERE

Remember the key difference: WHERE filters individual rows before they are grouped, while HAVING filters the summarized groups after they are processed. Mixing these up is one of the most common SQL mistakes beginners make.

Example: HAVING vs WHERE

sql
CREATE TABLE orders (id INT, region TEXT, total INT);
INSERT INTO orders VALUES (1, 'North', 500), (2, 'North', 50), (3, 'South', 800);
SELECT region, SUM(total) AS region_total
FROM orders
WHERE total > 0
GROUP BY region
HAVING SUM(total) > 300;

HAVING with Multiple Conditions

You can combine multiple group filters in a HAVING clause just like you would in WHERE. Use logical operators like AND and OR to connect your rules, such as requiring both a minimum order count and a minimum total spend.

Example: HAVING with Multiple Conditions

sql
CREATE TABLE orders (id INT, customer_id INT, total INT);
INSERT INTO orders VALUES (1, 1, 100), (2, 1, 200), (3, 2, 50);
SELECT customer_id, COUNT(*) AS order_count, SUM(total) AS spend
FROM orders
GROUP BY customer_id
HAVING COUNT(*) >= 2 AND SUM(total) > 200;

HAVING with Alias Values

MySQL allows you to reference alias column names defined in your SELECT clause inside the HAVING filter, unlike standard WHERE clauses in most databases. This keeps your queries readable by letting you reuse the same name instead of repeating the full aggregate expression.

Example: HAVING with Alias Values

sql
CREATE TABLE orders (id INT, category TEXT, total INT);
INSERT INTO orders VALUES (1, 'Books', 50), (2, 'Books', 60), (3, 'Toys', 10);
SELECT category, SUM(total) AS category_total
FROM orders
GROUP BY category
HAVING category_total > 100;

Complex Group Limits

HAVING can handle complex mathematical comparisons on aggregated values. It is excellent for identifying deviations and trends in your data, such as flagging categories whose average price jumped by more than a set threshold.

Example: Complex Group Limits

sql
CREATE TABLE prices (id INT, category TEXT, price INT);
INSERT INTO prices VALUES (1, 'Books', 10), (2, 'Books', 100), (3, 'Toys', 20);
SELECT category, AVG(price) AS avg_price
FROM prices
GROUP BY category
HAVING AVG(price) > 50;
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Using WHERE COUNT(*) > 1, which is invalid, when the filter on aggregates belongs in HAVING.
  2. Putting row filters in HAVING instead of WHERE, making the query slower.
  3. Writing HAVING before GROUP BY, which is a syntax error.
Chapter Summary
  • Aggregate functions such as COUNT(), SUM(), AVG(), MIN(), and MAX() summarize many rows into one value.
  • GROUP BY groups rows so aggregates can be calculated per group.
  • HAVING filters groups after aggregation.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.