Skip to main content

HAVING Clause

HAVING is for filtering groups, not raw rows. Think of it as WHERE for aggregate output.

WHERE vs HAVING

ClauseEvaluated onTypical use
WHERERaw rows before groupingWHERE status = 'paid'
HAVINGGrouped rows after aggregationHAVING SUM(total_amount) > 10000

Core Syntax

SELECT customer_id, SUM(total_amount) AS total_spend
FROM orders
WHERE order_date >= '2026-01-01'
GROUP BY customer_id
HAVING total_spend >= 5000;

Practical Example

Active High-Value Customers

SELECT customer_id,
COUNT(*) AS order_count,
SUM(total_amount) AS total_spend
FROM orders
WHERE status = 'paid'
GROUP BY customer_id
HAVING COUNT(*) >= 5
AND SUM(total_amount) >= 10000
ORDER BY total_spend DESC;
customer_idorder_counttotal_spend
10241124300.00
2031713120.00
4120610205.00

Common Pitfalls

PitfallConsequencePrevention
Using aggregate in WHERESyntax errorMove aggregate predicates to HAVING
Putting non-aggregate filters in HAVING onlyUnnecessary processingKeep row filters in WHERE, group filters in HAVING
Forgetting aliases in long expressionsHard-to-read queriesAlias aggregates and reuse aliases in HAVING (MySQL allows it)

Quick Reference

SELECT k, COUNT(*) c FROM t GROUP BY k HAVING c > 10;
SELECT k, SUM(v) s FROM t WHERE status = 'ok' GROUP BY k HAVING s >= 1000;
SELECT k, AVG(score) a FROM t GROUP BY k HAVING a BETWEEN 70 AND 90;

What's Next