SQL HAVING
Filters GROUPS after aggregation — the tool that finally makes 'channels where the average spend exceeds X' possible.
What & Why
WHERE filters individual rows, and it runs before grouping even happens — which means WHERE has no way to reference an aggregate result like AVG(spend), since that value doesn't exist yet at the point WHERE runs. HAVING solves this: it filters groups, after aggregation has already been computed, so conditions like "keep only groups where the average exceeds 50000" become possible for the first time.
Think of it this way: WHERE decides which raw rows are even allowed into the grouping process. HAVING decides which already-formed, already-summarized groups make it into the final output. They operate at two completely different stages of the query.
See How It Works
Marketing wants only channels with meaningful spend.
| id | name | channel | spend | start_date | end_date | status | target_segment |
|---|---|---|---|---|---|---|---|
| 1 | Spring Launch | google_ads | 55000.00 | 2024-01-15 | 2024-03-31 | active | smb |
| 2 | Retention Webinar | 45000.00 | 2024-02-10 | 2024-04-15 | active | enterprise | |
| 3 | Finance Retargeting | 50000.00 | 2024-03-12 | 2024-05-31 | active | enterprise | |
| 4 | Enterprise Search | google_ads | 60000.00 | 2024-04-01 | 2024-06-30 | active | enterprise |
SELECT
channel,
ROUND(SUM(spend), 2) AS total_spend
FROM marketing.campaigns
GROUP BY channel
HAVING SUM(spend) >= 10000
ORDER BY total_spend DESC;| channel | total_spend |
|---|---|
| google_ads | 115000.00 |
| 50000.00 | |
| 45000.00 |
HAVING keeps all three completed channel totals because each exceeds 10,000.
Sign up free to try it on a real business scenario