Modules / Database / HAVING Lab

HAVING in SQL

WHERE throws away rows before they are grouped. HAVING throws away groups after they are formed. Run both at once on the same table and watch the two stages do genuinely different work.

1 Rows — WHERE decides which ones even reach the grouping
2 Groups — aggregates computed from the surviving rows only

A struck-through amount is a row WHERE removed, so it never contributed to the total above it. That is the whole reason moving a condition between the two clauses changes the numbers rather than just the row count.

3 Result — the groups HAVING let through

Overview

Use WHERE for conditions on input rows. Use HAVING for conditions on groups, often involving an aggregate. For example, to find customers with at least three paid orders, remove unpaid orders first, then keep groups whose count reaches three.

Two filters, two stages

query.sqlSQLite
Result

The result contains one row per qualifying customer. The WHERE clause reduces the rows that must be grouped; the HAVING condition cannot be applied until the counts exist.

A common misplacement

A condition such as status = 'paid' belongs in WHERE when only paid rows should contribute to the count. Putting it in HAVING may require changing the grouping and can produce confusing results. Conversely, filtering on COUNT(*) in WHERE is invalid because that aggregate does not exist at the row-filter stage.

Some engines allow HAVING without GROUP BY, treating the input as one group; for portable SQL, use it with explicit grouping. Select the grouping key alongside aggregates so each output row is interpretable. See GROUP BY and aggregate functions.

Check the intended grain

Before adding HAVING, state what one result row should represent: a customer, month, product, or another key. Then verify the grouping columns and test a small dataset where one group is just below the threshold and another just above it. That catches off-by-one filters and accidental grouping by extra columns.

Example: filter before and after grouping

Assume a customer has four orders, but only two are paid. With WHERE status = 'paid' and HAVING COUNT(*) >= 3, that customer is excluded because the group contains two qualifying rows. If instead all four orders should count but the customer must have at least one paid order, calculate both measures with conditional aggregation and filter on the paid count. Writing down the population for each metric prevents a subtle mistake where WHERE removes rows needed for another aggregate.

Avoid changing the population accidentally

A filter before aggregation changes which rows contribute to every aggregate in the group. A HAVING predicate changes which completed groups are returned, but does not change the values already calculated inside those groups. If a query needs both all-order revenue and paid-order count, do not filter away unpaid rows in WHERE. Use conditional expressions or filtered aggregates for each measure, then apply HAVING to the calculated group values. This makes the data population for each metric explicit.

Further reading

Predict, then reveal

About to run: set HAVING Threshold to its minimum (0). Before it does — what happens to the readout?

Committing to an answer first is the point — the reveal runs the experiment on the visualisation above and reads the real value back, so nothing here is scripted.

Recall check

0 of 3

Say the answer out loud before you reveal it — recalling it is what makes it stick, and rereading it is not.

  1. What does this module say about “Two filters, two stages”?

  2. What does this module say about “A common misplacement”?

  3. What does this module say about “Check the intended grain”?

Cheat sheet

HAVING in SQL

Rows become groups become filtered groups. Move the same condition between WHERE and HAVING and watch the answer change, one stage at a time.

DATABASE · vizlearn.in/database/having_in_sql.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.