VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
WHERE drops rows before they are grouped. HAVING drops whole groups after
they are built. That 1 sentence is the whole lesson, and everything below is a
consequence of it.
Open the client with mysql -u root -p sakila before you start.
How HAVING works
GROUPING_COLUMN with the column that defines the groups, TABLE_NAME
with the table, AGGREGATE_FUNCTION(COLUMN_NAME) with what to work out per
group, and GROUP_CONDITION with a test that applies to a whole group, such as
COUNT(*) > 200.
The server builds every group first, then keeps the groups where the condition
is true.
Examples
1) Keep only the large groups
2) HAVING can use an alias
An alias names a column of the result, and by the timeHAVING runs the result
exists.
WHERE cannot do this, because it runs
before there is a result to name: the WHERE lesson shows the
ERROR 1054 you get instead.
3) Both clauses in one query
Most real queries use both:WHERE to choose the rows, HAVING to choose the
groups.
WHERE kept the films over 120 minutes, the grouping counted them per rating,
and HAVING kept the 1 rating with more than 100 of them.
An aggregate cannot go in WHERE
Move thatHAVING condition into WHERE and the server refuses it outright.
WHERE runs before the grouping, so there is no count for it to test. The same
applies to the alias of an aggregate, with a different error.
HAVING.
ERROR 1054 on an alias in WHERE has 2 remedies, and the aggregate is what
tells them apart. An alias for a plain expression can be repeated in WHERE,
which the WHERE lesson shows. An alias for an aggregate
cannot be repeated there at all, because of ERROR 1111 above, so it has to
move to HAVING.
Which clause tests what
Put a condition in
WHERE whenever it tests a row, and keep HAVING for what
only makes sense once a group exists. That is also the cheaper arrangement, for
reasons the
MySQL GROUP BY performance guide measures.
Naming a select-list alias in
HAVING is a MySQL extension. Repeat the
aggregate rather than the alias when the same SQL has to run on another
database.Summary
- Use
HAVINGto drop whole groups, andWHEREto drop rows. - Expect
HAVINGto run afterGROUP BY, so it can test an aggregate. - Read
ERROR 1111as an aggregate inWHEREthat belongs inHAVING. - Name a column alias in
HAVING, whichWHEREcannot do. - Put a condition in
WHEREwhen it tests a row rather than a group.
See also
- GROUP BY — building the groups this clause filters
- WHERE — the clause that runs first
- Aggregate functions — what a group condition usually tests
- MySQL GROUP BY performance — the cost of grouping before filtering

