Aggregation basics

SQL GROUP BY and HAVING for Team Metrics

Learn how GROUP BY and HAVING create reliable summary metrics by segment, month, or product class, including checks that avoid incorrect totals and hidden bias.

How this page is maintained

Written for learners, checked against the sources below, and reviewed every year. Last reviewed July 22, 2026.

Short answer

GROUP BY collapses rows into categories and aggregate functions compute metrics per group. HAVING filters grouped results after aggregation, unlike WHERE which filters raw rows first.

  • Use WHERE for row-level filters before grouping and HAVING for aggregate filters after grouping.
  • Name each aggregate clearly so reports read like business statements.
  • Validate grouped totals against a raw baseline for the same period.

Control aggregation scope

At Northstar, finance tracks paid order revenue by customer segment each month. The query should filter to paid orders in WHERE, then group by segment and month so the metric aligns with accounting rules.

If you group by too many columns, you can accidentally produce near-raw output and miss the intended summary level.

Use HAVING for threshold questions

HAVING is useful for questions like segments with more than 100 orders. It evaluates after grouping, so it can reference COUNT, SUM, or AVG results.

Keep threshold values in visible parameters when possible. Hard-coded cutoffs are easy to forget and can drift from policy.

  • DATE_TRUNC helps group timestamps into month buckets.
  • COUNT(DISTINCT ...) can prevent duplicate-key inflation.
  • Round only at presentation time to avoid cumulative math drift.

Find high-volume segments in Q1

Orders table fields include order_id, customer_id, segment, order_date, status, and total_amount.

  1. Filter WHERE status = 'paid' and order_date between Q1 boundaries.
  2. GROUP BY segment and compute COUNT(*) as paid_orders plus SUM(total_amount) as paid_revenue.
  3. Apply HAVING COUNT(*) >= 200 to keep only high-volume segments.
Result: The report returns only segments that cleared the Q1 volume threshold, with paired order counts and revenue.

Common mistakes

  • Using HAVING for row-level filters that belong in WHERE.
  • Grouping by display text that changes over time instead of stable codes.
  • Comparing rounded aggregates during validation.
  • Mixing paid and pending statuses in one revenue metric by mistake.

Try one

When should a condition like SUM(total_amount) > 50000 be placed in HAVING instead of WHERE?

Use HAVING because SUM(total_amount) is an aggregate that exists only after rows are grouped.

Sources

Learn this with a tutor

Tell LearnLive what you already know and what you need to do with group by having.

Build this course