SQL: WHERE versus HAVING
Posted by jpluimers on 2025/07/08
While writing Some SQLite things I recently learned a while ago I bumped into SQLite extending the SQL syntax allowing SELECT column aliases in WHERE and ON conditions of JOIN clauses whereas standard SQL only allows it in the HAVING clause (some other DMBS allow this as well).
It reminded I never wrote a blog post about WHERE versus HAVING, which can be phrased into just two points:
WHEREis applied before theSELECTis assembledHAVINGis applied after theSELECTis assembled
This also means WHERE has a lot more influence on performance than HAVING, so using HAVING just so you can use aliases there instead of in the WHERE clauses is not a smart idea unless you verify in the query plan and query optimisation output of our DBMS this has no influence (some DBMS even allow you to see the results of query rewriting which you can use to verify if two queries really are equivalent from the DBMS perspective).
Since HAVING can be used without GROUP BY, many people work around the WHERE alias limitation. Given the above effects on performance, this is not a wise idea, despite the same end-results. See the example below that uses the [Wayback/Archive] MySQL :: Sakila Sample Database: customer table.
Luckily there has been enough other material explaining WHERE versus HAVING, so I can reference it from here, for instance (note note limited to MySQL/MariaDB) [Wayback/Archive] mysql – WHERE vs HAVING – Stack Overflow (thanks [Wayback/Archive] baloo and [Wayback/Archive] Fishdrowned):
Q
Why do you need to place columns you create yourself (for exampleselect 1 as "number") afterHAVINGand notWHEREin MySQL?And are there any downsides instead of doingWHERE 1(writing the whole definition instead of a column name)?A
…
You will get exactly the same results, you can see theHAVINGclause can work without GROUP BY clause.
Here’s the difference:SELECT `value` v FROM `table` WHERE `v`>5;The above query will raise error: Error #1054 – Unknown column ‘v’ in ‘where clause’SELECT `value` v FROM `table` HAVING `v`>5; -- Get 5 rowsWHEREclause allows a condition to use any table column, but it cannot use aliases or aggregate functions.HAVINGclause allows a condition to use a selected (!) column, alias or an aggregate function.This is becauseWHEREclause filters data before select, butHAVINGclause filters resulting data after select.So put the conditions inWHEREclause will be more efficient if you have many many rows in a table.TryEXPLAINto see the key difference
I got there via [Wayback/Archive] sql – Using column alias in WHERE clause of MySQL query produces an error – Stack Overflow (thanks [Wayback/Archive] James, [Wayback/Archive] victor hugo, [Wayback/Archive] DTs, [Wayback/Archive] Millie Smith, and [Wayback/Archive] Pacerier):
Q
The query I’m running is as follows, however I’m getting this error:#1054 - Unknown column 'guaranteed_postcode' in 'IN/ALL/ANY subquery'SELECT `users`.`first_name`, `users`.`last_name`, `users`.`email`, SUBSTRING(`locations`.`raw`,-6,4) AS `guaranteed_postcode` FROM `users` LEFT OUTER JOIN `locations` ON `users`.`id` = `locations`.`user_id` WHERE `guaranteed_postcode` NOT IN #this is where the fake col is being used ( SELECT `postcode` FROM `postcodes` WHERE `region` IN ( 'australia' ) )My question is: why am I unable to use a fake column in the where clause of the same DB query?A
…
As pointed in the comments, using HAVING instead may do the work. Make sure to give a read at this question too: WHERE vs HAVING.
C
This may or may not be important in your case, but
HAVINGexecutes slower thanWHEREC
The reason
havingworks is because the column values have to be computed by the time you get to thehaving. This isn’t the case withwhere, as stated above.C
@MillieSmith, That’s true only for aggregate predicates in
having. As we know,havingcan have non-aggregate predicates, just likewhere. In such a case, it isn’t hard for the optimizer to convert thathavingpredicate to an expandedwherepredicate. As such, citation needed. You can find it here: stackoverflow.com/a/18710763/632951
HAVING without GROUP BY?
Being taught SQL the classic way, it took several years for me to realise that HAVING can be used without GROUP BY.
In the current day and age of learning computer languages by searching for quick results, I an sort of glad that the “classic” way at the time of writing is still explained at HAVING (SQL) – Wikipedia
A
HAVINGclause in SQL specifies that an SQLSELECTstatement must only return rows where aggregate values meet the specified conditions.
HAVINGandWHEREare often confused by beginners, but they serve different purposes.WHEREis taken into account at an earlier stage of a query execution, filtering the rows read from the tables. If a query containsGROUP BY, rows from the tables are grouped and aggregated. After the aggregating operation,HAVINGis applied, filtering out the rows that don’t match the specified conditions. Therefore,WHEREapplies to data read from tables, andHAVINGshould only apply to aggregated data, which isn’t known in the initial stage of a query.
Anyway, these two have a different query plan where the HAVING uses more resources as no candidate keys or indices is used: [Wayback/Archive] MySQL 8.0 | dbfiddle:
explainselect *from customerwhere store_id = 1and active = 0
id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE customer null ref idx_fk_store_id idx_fk_store_id 1 const 326 10.00 Using where explainselect *from customerhaving store_id = 1and active = 0
id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE customer null ALL null null null null 599 100.00 null
But they give equivalent results as you can see in [Wayback/Archive] MySQL 8.0 | dbfiddle:
select *from customerwhere store_id = 1and active = 0
customer_id store_id first_name last_name address_id active create_date last_update 124 1 SHEILA WELLS SHEILA.WELLS@sakilacustomer.org 128 0 2006-02-14 22:04:36 2006-02-15 04:57:20 271 1 PENNY NEAL PENNY.NEAL@sakilacustomer.org 276 0 2006-02-14 22:04:36 2006-02-15 04:57:20 368 1 HARRY ARCE HARRY.ARCE@sakilacustomer.org 373 0 2006-02-14 22:04:37 2006-02-15 04:57:20 406 1 NATHAN RUNYON NATHAN.RUNYON@sakilacustomer.org 411 0 2006-02-14 22:04:37 2006-02-15 04:57:20 482 1 MAURICE CRAWLEY MAURICE.CRAWLEY@sakilacustomer.org 487 0 2006-02-14 22:04:37 2006-02-15 04:57:20 534 1 CHRISTIAN JUNG CHRISTIAN.JUNG@sakilacustomer.org 540 0 2006-02-14 22:04:37 2006-02-15 04:57:20 558 1 JIMMIE EGGLESTON JIMMIE.EGGLESTON@sakilacustomer.org 564 0 2006-02-14 22:04:37 2006-02-15 04:57:20 592 1 TERRANCE ROUSH TERRANCE.ROUSH@sakilacustomer.org 598 0 2006-02-14 22:04:37 2006-02-15 04:57:20 from customerhaving store_id = 1and active = 0
customer_id store_id first_name last_name address_id active create_date last_update 124 1 SHEILA WELLS SHEILA.WELLS@sakilacustomer.org 128 0 2006-02-14 22:04:36 2006-02-15 04:57:20 271 1 PENNY NEAL PENNY.NEAL@sakilacustomer.org 276 0 2006-02-14 22:04:36 2006-02-15 04:57:20 368 1 HARRY ARCE HARRY.ARCE@sakilacustomer.org 373 0 2006-02-14 22:04:37 2006-02-15 04:57:20 406 1 NATHAN RUNYON NATHAN.RUNYON@sakilacustomer.org 411 0 2006-02-14 22:04:37 2006-02-15 04:57:20 482 1 MAURICE CRAWLEY MAURICE.CRAWLEY@sakilacustomer.org 487 0 2006-02-14 22:04:37 2006-02-15 04:57:20 534 1 CHRISTIAN JUNG CHRISTIAN.JUNG@sakilacustomer.org 540 0 2006-02-14 22:04:37 2006-02-15 04:57:20 558 1 JIMMIE EGGLESTON JIMMIE.EGGLESTON@sakilacustomer.org 564 0 2006-02-14 22:04:37 2006-02-15 04:57:20 592 1 TERRANCE ROUSH TERRANCE.ROUSH@sakilacustomer.org 598 0 2006-02-14 22:04:37 2006-02-15 04:57:20
--jeroen






Leave a comment