The HAVING clause specifies conditional criteria that filter the summary groups produced by a GROUP BY clause.
It exists because the standard SQL query processing pipeline evaluates the WHERE clause before aggregation occurs.
Because WHERE executes prior to row grouping, it cannot reference aggregate metrics like sums, counts, or averages.
The HAVING clause executes after grouping and aggregation are complete, providing a filter on the resulting summary rows.
For example, filtering for departments where total sales exceed fifty thousand pounds requires a HAVING condition on SUM of sales.
Conditions inside HAVING can reference any aggregate function, regardless of whether that function appears in the SELECT clause.
While HAVING can technically evaluate unaggregated columns, doing so is poor practice and should be handled in WHERE.
Filtering rows in WHERE before grouping is computationally more efficient, as it shrinks the volume of data entering aggregation.
A query can contain both WHERE and HAVING clauses simultaneously, working at distinct stages of execution.
In such queries, WHERE eliminates unwanted raw rows first, GROUP BY aggregates the survivors, and HAVING filters the groups.
For instance, filtering orders from twenty twenty-four in WHERE and filtering customers with over five orders in HAVING.
Neglecting to use WHERE for row-level filters forces the database to aggregate unnecessary records, wasting memory and processing time.
The conceptual execution order of SQL is FROM, followed by WHERE, GROUP BY, HAVING, SELECT, and finally ORDER BY.
Remembering this execution lifecycle clarifies why column aliases created in SELECT cannot be referenced inside HAVING.
Analysts should always place row predicates in WHERE and reserve HAVING strictly for conditions that require aggregate metrics.