GROUP BY and HAVING

Site Admin · 11 Sep 2026 · 2 views

Splitting Rows Into Groups

GROUP BY divides rows into groups, then an aggregate runs once per group - one output row per group.

SELECT city, COUNT(*) AS members
FROM customers
GROUP BY city;

SELECT customer_id, SUM(total) AS lifetime_value
FROM orders
GROUP BY customer_id
ORDER BY lifetime_value DESC;

The Golden Rule

Every column in the SELECT list that is not inside an aggregate function must appear in the GROUP BY clause.

-- WRONG: country not allowed unless it is grouped
SELECT city, country, COUNT(*) FROM customers GROUP BY city;

-- RIGHT
SELECT city, country, COUNT(*) FROM customers GROUP BY city, country;

Filtering Groups With HAVING

HAVING filters the groups after aggregation, the way WHERE filters rows before it.

SELECT customer_id, SUM(total) AS lifetime
FROM orders
GROUP BY customer_id
HAVING SUM(total) > 1000;

SELECT city, COUNT(*) AS c
FROM customers
GROUP BY city
HAVING COUNT(*) >= 5;

WHERE vs HAVING

  • WHERE runs before grouping; it cannot use aggregates.
  • HAVING runs after grouping; it can use aggregates.
  • Filter early with WHERE for speed, and use HAVING only for group conditions.

Key Points

  • GROUP BY creates one output row per group.
  • All non-aggregated SELECT columns must be grouped.
  • HAVING filters groups; WHERE filters rows.
  • This is how every sales, traffic and user report starts.
Share this post:

Comments (0)

Please login or register to comment.