SQL WHERE vs HAVING
They look similar and both filter — but they operate at completely different stages of the query, and mixing up when to use which is one of the most common intermediate-level mistakes.
What & Why
Both WHERE and HAVING remove rows from a result — that's what makes them easy to confuse. The difference is entirely about when each one runs, relative to GROUP BY:
A useful way to remember it: if the condition needs an aggregate function (COUNT, SUM, AVG, and so on) to even make sense, it belongs in HAVING. If it's checking a plain column value on an individual row, it belongs in WHERE. A single query can absolutely use both together — and when it does, the order they run in genuinely changes the result, not just the syntax.
See How It Works
Marketing wants channels with at least two campaigns whose spend is 50,000 or more.
| 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 |
| id | name | channel | spend | WHERE result |
|---|---|---|---|---|
| 1 | Spring Launch | google_ads | 55,000 | WAIT |
| 2 | Retention Webinar | 45,000 | WAIT | |
| 3 | Finance Retargeting | 50,000 | WAIT | |
| 4 | Enterprise Search | google_ads | 60,000 | WAIT |
WHERE and HAVING have not run yet; all four campaigns are source rows.
SELECT
channel,
COUNT(*) AS qualifying_campaigns
FROM marketing.campaigns
WHERE spend >= 50000
GROUP BY channel
HAVING COUNT(*) >= 2
ORDER BY qualifying_campaigns DESC, channel;Practice this concept
Marketing wants channels with at least two campaigns whose spend is 50,000 or more.
marketingPrefix tables with marketing.table_name.
channelqualifying_campaignsmarketing.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