Skip to main content

Command Palette

Search for a command to run...

📊 PostgreSQL – Filtering Groups with HAVING

Updated
4 min read
M

As a former 3D Animator with more than 12 years of experience, I have always been fascinated by the intersection of technology and creativity. That's why I recently shifted my career towards MERN stack development and software engineering, where I have been serving since 2021.

With my background in 3D animation, I bring a unique perspective to software development, combining creativity and technical expertise to build innovative and visually engaging applications. I have a passion for learning and staying up-to-date with the latest technologies and best practices, and I enjoy collaborating with cross-functional teams to solve complex problems and create seamless user experiences.

In my current role as a MERN stack developer, I have been responsible for developing and implementing web applications using MongoDB, Express, React, and Node.js. I have also gained experience in Agile development methodologies, version control with Git, and cloud-based deployment using platforms like Heroku and AWS.

I am committed to delivering high-quality work that meets the needs of both clients and end-users, and I am always seeking new challenges and opportunities to grow both personally and professionally.

So far in SQL, we’ve learned these keywords:

FROM
JOIN
WHERE
GROUP BY

Now we’re introducing a new keyword:

🔥 HAVING

At first, HAVING looks very similar to WHERE.

But there is one very important difference.


🧠 WHERE vs HAVING (Very Important)

Keyword Filters What?
WHERE Filters rows
HAVING Filters groups

This is the most important rule.


🧩 Order of SQL Execution

When writing grouped queries, SQL follows this order:

SELECT
FROM
JOIN
WHERE
GROUP BY
HAVING

Notice something important:

👉 HAVING always comes after GROUP BY.

And here’s another rule:

You will never see HAVING without GROUP BY.

You can use GROUP BY without HAVING.
But if you want to filter groups, you must use HAVING.


🎯 Practical Example

Let’s solve a real problem.

🧠 Goal:

Find the number of comments for each photo
Where:

  • The photo_id is less than 3

  • The photo has more than 2 comments

Notice something interesting:

There are two different filters here.


🔎 First Filter: photo_id < 3

This filter checks a condition on individual rows.

That means we use:

WHERE

Because WHERE filters rows.


🔎 Second Filter: Photo has more than 2 comments

This requires:

  • GROUP BY photo_id

  • COUNT(*)

  • Filtering based on COUNT

Since COUNT is an aggregate function, this filter must use:

HAVING

Because HAVING filters groups after aggregation.


🧠 Step-by-Step Mental Model

Imagine our comments table:

id photo_id
1 1
2 1
3 1
4 2
5 3
6 4

Step 1️⃣ WHERE Phase (Row Filtering)

We apply:

WHERE photo_id < 3

Now we remove rows where photo_id is 3 or 4.

Remaining rows:

id photo_id
1 1
2 1
3 1
4 2

Step 2️⃣ GROUP BY Phase

We group by photo_id.

Group 1:

  • 3 comments

Group 2:

  • 1 comment

Step 3️⃣ HAVING Phase (Group Filtering)

We apply:

HAVING COUNT(*) > 2

Group 1 → 3 comments ✅ keep
Group 2 → 1 comment ❌ remove

Final result:

photo_id count
1 3

🧾 Final Query

Here is the complete SQL query:

SELECT photo_id, COUNT(*)
FROM comments
WHERE photo_id < 3
GROUP BY photo_id
HAVING COUNT(*) > 2;

🧠 Why Not Use WHERE Instead of HAVING?

This will NOT work:

WHERE COUNT(*) > 2

Because:

  • WHERE runs before GROUP BY

  • COUNT() doesn’t exist yet at that stage

Aggregation happens after grouping.

That’s why HAVING exists.


🏗 Execution Flow (Super Important Concept)

Here’s how PostgreSQL processes it internally:

  1. FROM → get table

  2. WHERE → filter rows

  3. GROUP BY → create groups

  4. COUNT/SUM/etc → calculate aggregates

  5. HAVING → filter groups

  6. SELECT → return final result


🔥 When Do We Use HAVING?

Use HAVING when:

  • You are using GROUP BY

  • You want to filter based on an aggregate function

  • You need conditions like:

    • COUNT(*) > 5

    • SUM(amount) > 1000

    • AVG(score) > 70


🧠 Real-World Business Examples

🛒 Find customers who placed more than 5 orders:

SELECT user_id, COUNT(*)
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 5;

💰 Find products generating more than $10,000 revenue:

SELECT product_id, SUM(price)
FROM sales
GROUP BY product_id
HAVING SUM(price) > 10000;

🎯 Final Summary

WHERE:

Filters individual rows.

GROUP BY:

Creates groups.

HAVING:

Filters groups after aggregation.


🧪 Practice Questions for Students

  1. Find departments with more than 10 employees.

  2. Find users whose total spending is greater than 5000.

  3. Find cities where average salary is above 100,000.

  4. Find products sold more than 50 times.