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.
Practice this concept
Marketing wants campaign channels whose total spend is at least 10,000. Return channel and total_spend, ordered from highest total spend to lowest.
marketingPrefix tables with marketing.table_name.
channeltotal_spendmarketing.campaigns| Column | Type |
|---|---|
| id | integer |
| name | text |
| channel | text |
| spend | numeric |
| start_date | date |
| end_date | date |
| status | text |
| target_segment | text |
| legacy_id | text |
Sign up free to try it on a real business scenario