The labs on this site need JavaScript to run. The same content is written out below.
Learn › Database › SQL
SELECT processing order
Why can't you use an alias or an aggregate function in WHERE?
You can think of a SELECT statement as being processed not in the order it is written but in the order FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT. Which names you can use depends on where in this order a clause comes.
Checked against real execution · The SQL for the 32 combinations was run as is on MySQL 8.4.11, and the error numbers and messages and every row of the final results were checked against the results. Differences from standard SQL follow the MySQL documentation
Concepts in this lab
- Logical processing order of the SELECT statement
- The order in which the clauses of a SELECT statement are considered to be processed when you work out its result. It is FROM (including JOIN) → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT, which differs from the order in which you write the clauses. The only names you can use in a clause are those that were created at a step processed before that clause. So an alias given in SELECT cannot be used in WHERE but can be used in ORDER BY, and an aggregate function can be used from HAVING on, after the groups have been made. This does not mean that the database actually runs the statement in this order. The optimizer changes the execution order as long as the result stays the same.
- GROUP BY, WHERE, HAVING
- GROUP BY groups rows that have the same value in the specified columns and computes an aggregate (such as a sum or a count) for each group. WHERE filters individual rows before they are grouped, and HAVING filters groups after grouping. So put conditions on aggregate results in HAVING and all other conditions in WHERE.
- Column alias (AS)
- Another name that you give to a column or an expression in the SELECT list. You write it as SUM(amount) AS total, and it becomes the column name in the result. An alias is created at the SELECT step, so it can be used in ORDER BY, which is processed after SELECT, but not in WHERE, which is processed before it. MySQL also accepts an alias in GROUP BY and HAVING, but this is an extension that standard SQL does not have, so it can cause an error in other databases. To filter on the same value in WHERE, write the expression again instead of the alias.
- Aggregate function
- A function that takes the values of several rows and returns a single value. COUNT (number of rows), SUM (total), AVG (average), MAX (largest value), and MIN (smallest value) are typical. With GROUP BY, it is computed once for each group. Without GROUP BY, all the rows that satisfy the condition are treated as one group. An aggregate function can be computed only after the groups are made, so it cannot be used in WHERE. You use it in HAVING, SELECT, and ORDER BY. Aggregate functions other than COUNT(*) skip NULL values when they compute.
Try these
- Default settings — The alias total is used in WHERE. Error 1054 occurs.
- The same alias in HAVING — It runs in MySQL. It is an extension that standard SQL does not have.
- An aggregate function in WHERE — There are no groups yet, so error 1111 occurs.
- An aggregate function in HAVING — The 2 members whose total is 300 or more come out.
- "300 or more" in WHERE — Orders are filtered one by one, so Alice's total becomes 300.
- A row column in HAVING — After grouping, there is no single amount, so error 1054 occurs.
- Grouping by an alias, sorting, and limiting — Aliases in GROUP BY and ORDER BY, and LIMIT at the very end.
Running with the default settings
- FROM orders: This is processed first. It takes all 6 rows of the orders table. At this point the only names you can use are the table columns id, member, and amount.
- WHERE total >= 300: total is an alias that SELECT gives to SUM(amount), but SELECT is processed after WHERE. At this point the name total does not exist yet, so MySQL rejects the statement with error 1054 (Unknown column 'total' in 'where clause').
- A statement that fails returns no rows at all, and the later steps are not processed. MySQL raises this error while it analyzes the statement, before it starts executing it, so in practice it stops before it even reads the table.
Result of this run
- An alias is attached at the SELECT step, so it cannot be used in WHERE, which is processed earlier. This is true in standard SQL as well as in MySQL. To filter on an aggregate, move the condition to HAVING, and in standard SQL write the aggregate expression again, as in HAVING SUM(amount) >= 300, instead of the alias (accepting an alias in HAVING is a MySQL extension).
Code
CREATE TABLE orders (
id INT PRIMARY KEY,
member VARCHAR(20) NOT NULL,
amount INT NOT NULL
);
INSERT INTO orders (id, member, amount) VALUES
(1, 'Alice', 300),
(2, 'Alice', 150),
(3, 'Bob', 120),
(4, 'Bob', 200),
(5, 'Carol', 250),
(6, 'Dave', 80);
-- Processing order: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
SELECT member, SUM(amount) AS total
FROM orders
WHERE total >= 300
GROUP BY member;
Assumptions of this model
This is a small example with 6 orders. For all 32 combinations (4 conditions × 2 places to write them × the GROUP BY alias × sorting and limiting), the SQL shown on the screen was run as is on a real MySQL 8.4.11 (default sql_mode, including ONLY_FULL_GROUP_BY), and the error numbers and messages, every row of the final result, and the number of rows counted with the WHERE condition alone were checked against the model and matched. For combinations without ORDER BY, the order in which the rows come out is not guaranteed, so only the contents of the rows were compared, not their order. The seven steps are a logical order: the result is the same as if the statement were processed in this order. Real MySQL raises an error in the analysis phase, before it executes the statement, if it cannot find a name, so for a statement that raises an error, the earlier steps are not executed either. The optimizer may also change the actual execution order, but the result is the same as processing in the logical order. That GROUP BY and HAVING accept the alias from SELECT was checked by running it on MySQL. That this is an extension that standard SQL does not have follows not from running it but from the sections "MySQL Handling of GROUP BY" and "Problems with Column Aliases" of the MySQL 8.4 Reference Manual. What happens in other databases was not run on the PC used to write this lab. DISTINCT and window functions inside the SELECT clause, and the place where JOIN is processed, are not covered in this lab (the results of JOIN and GROUP BY are covered in the "JOIN and GROUP BY" lab).
Previous lab: Normalization · Next lab: JOIN and GROUP BY