Why can't I use an alias for an aggregate in a having clause?
In MS SQL, the only place (I'm aware of) that you can reference aliases is in the ORDER BY clause. The ability to reference aliases in other parts of the query is a feature that many other db platforms have and honestly it annoys me that Microsoft hasn't considered it a useful enough feature to add it.
Using alias in the WHERE and HAVING statements?
Only MySQL permits alises in HAVING
, it is not standard SQL (see here: https://dba.stackexchange.com/questions/50391/why-does-mysql-allow-having-to-use-select-aliases ) please note that no other major RDBMS allows the use of aliases in WHERE
or HAVING
.
The reason you can't use aliases in WHERE
(and HAVING
) is because SELECT
is actually evaluated after most other sub-clauses: https://stackoverflow.com/a/21693272/159145
A
SELECT
query is evaluated, conceptually, in the following order:
- The
FROM
clause- The
WHERE
clause- The
GROUP BY
clause- The
HAVING
clause- The
SELECT
clause- The
ORDER BY
clause
So your query:
SELECT
customer_id,
address_id AS addressID
FROM
customer
WHERE
addressID = 5
Is evaluated in this order:
1: FROM
customer
2: WHERE
address_id = 5
3: SELECT
customer_id,
address_id AS addressID
As you cans see, if the WHERE
part referenced addressID
instead of address_id
the query execution engine would complain because addressID
is not defined at that point.
MySQL does permit the referencing of (normal) aliases in HAVING
by doing a (non-standard) neat trick where it partially evaluates the SELECT
before it evaluates HAVING
- and because MySQL has a handling of aliases that means the evaluation engine can be sure that the alias is valid (which is why most other RDBMS engines don't allow the use of aliases in HAVING
when they otherwise should be able to). But you can't use an alias in WHERE
because if there's a GROUP BY
then it might render an alias meaningless, consider:
SELECT
SUM( foo ) AS baz,
created
FROM
foo
WHERE
baz > 5 -- Meaningless: the GROUP BY hasn't been evaluated yet, so `baz` is unavailable
GROUP BY
created
MySQL explains this in their manual: https://dev.mysql.com/doc/refman/5.7/en/problems-with-alias.html
Standard SQL disallows references to column aliases in a
WHERE
clause. This restriction is imposed because when theWHERE
clause is evaluated, the column value may not yet have been determined.The
WHERE
clause determines which rows should be included in theGROUP BY
clause, but it refers to the alias of a column value that is not known until after the rows have been selected, and grouped by theGROUP BY
.
Why can't I use alias in a count(*) column and reference it in a having clause?
See the document referenced by CodeByMoonlight in an answer to your recent question.
The HAVING clause is evaluated before the SELECT - so the server doesn't yet know about that alias.
- First the product of all tables in the from clause is formed.
- The where clause is then evaluated to eliminate rows that do not satisfy
the search_condition.- Next, the rows are grouped using the columns in the group by clause.
- Then, Groups that do not satisfy the search_condition in the having
clause are eliminated.- Next, the expressions in the select clause target list are
evaluated.- If the distinct keyword in present in the select clause, duplicate rows
are now eliminated.- The union is taken after each sub-select is evaluated.
- Finally, the resulting rows are sorted according to the columns
specified in the order by clause.
Referring to a select aggregate column alias in the having clause in Postgres
Is there a setting in Postgres that will allow it to use column aliases in the having clause?
No. Implementations that allow references to SELECT
-list entries in HAVING
are going outside the standard.
You should use a subquery, e.g.
select
c
from (
select
sum(clicks) c
from table
where event_date >= '1999-01-01'
group by keyword_id
) x
where c > 10;
... or repeat the aggregate.
Column ALIAS not recognized in HAVING clause, unkown column error
What database is this? If it's something like MSSQL, you can't use aliases like that elsewhere in a query. You'll have to replicate the whole alias definition:
SELECT big+ugly+calculation AS foo
...
HAVING (big+ugly_calculation) = bar
Or wrap the query in another:
SELECT *
FROM ( SELECT *, big+ugly+calculation AS foo )
WHERE foo = bar
SQL aggregate function alias
The manual clarifies:
An output column's name can be used to refer to the column's value in
ORDER BY
andGROUP BY
clauses, but not in theWHERE
orHAVING
clauses;
there you must write out the expression instead.
Bold emphasis mine.
You can avoid typing long expressions repeatedly with a subquery or CTE:
SELECT state_name, no_big_city, big_city_population
FROM (
SELECT s.name AS state_name
, COUNT(*) FILTER (WHERE p.type = 'city' AND p.population >= 100000) AS no_big_city
, SUM(population) FILTER (WHERE p.type = 'city' AND p.population >= 100000) AS big_city_population
FROM state s
JOIN place p ON s.code = p.state_code
GROUP BY s.name -- can be input column name as well, best schema-qualified to avoid ambiguity
) sub
WHERE no_big_city >= 5
OR big_city_population >= 1000000
ORDER BY state_name;
While being at it, I simplified with the aggregate FILTER
clause (Postgres 9.4+):
- How can I simplify this game statistics query?
However, I suggest this simpler and faster query to begin with:
SELECT s.state_name, p.no_big_city, p.big_city_population
FROM state s
JOIN (
SELECT state_code AS code -- alias just to simplify join
, count(*) AS no_big_city
, sum(population) AS big_city_population
FROM place
WHERE type = 'city'
AND population >= 100000
GROUP BY 1 -- can be ordinal number referencing position in SELECT list
HAVING count(*) >= 5 OR sum(population) >= 1000000 -- simple expressions now
) p USING (code)
ORDER BY 1; -- can also be ordinal number
I am demonstrating another option to reference expressions in GROUP BY
and ORDER BY
. Only use that if it doesn't impair readability and maintainability.
difference between where and having with respect to aliases
Basically because they where defined for different purposes. The WHERE
clause is for records filtering and the HAVING
clause is designed for filtering with aggregate functions (GROUP BY
).
In your second query an implicit GROUP BY
filtering is being used, so for instance, if you add another column to the SELECT
clause you will end up with different results.
EDIT based on correction by Martin Smith
HAVING
was created to allow filtering of rows resulting of a GROUP BY
. When no GROUP BY
is specified, the whole result is considered a group.
If neither a
<where clause>
nor a<group by clause>
is specified,
then let T be the result of the preceding<from clause>
or
...the group is the
entire table if no<group by clause>
is specified
EDIT 2
Now regarding the ALIAS:
The specification for the WHERE clause regarding the columns references in the search condition says this:
Each
<column reference>
directly contained in the<search
shall
condition>
unambiguously reference a column of T or be an outer reference.
Refer to: 7.6 <where clause>
, Syntax Rule 1.
The specification for the HAVING clause regarding the columns references in the search condition says this:
Each
<column reference>
directly contained in the<search
shall unambiguously reference a grouping column of T
condition>
or be an outer reference.
Refer to: 7.8 <having clause>
, Syntax Rule 1.
And a grouping column is defined as:
A column referenced in a
<group by clause>
is a grouping column.
So in conclusion the WHERE
must reference a column of the table and the HAVING
clause must reference a grouping column of the group of rows.
(Second Informal Review Draft) ISO/IEC 9075:1992, Database
Language SQL- July 30, 1992
Aggregate Function alias from select used in where clause
You can not use column aliases in your WHERE clause. Also, when comparing results of aggregations, you must use the HAVING clause, after the GROUP BY is applied.
Do something like this:
SELECT Users.UserID,
ClassCourses.CourseID,
ClassCourses.MinMark,
sum(StudentMark) as SMark
FROM Users
INNER JOIN UserExams ON Users.UserID = UserExams.UserID
INNER JOIN Exams ON UserExams.ExamID = Exams.ExamID
INNER JOIN ClassCourses ON Exams.ClassID = ClassCourses.ClassID AND Exams.CourseID = ClassCourses.CourseID
GROUP BY Users.UserID, ClassCourses.CourseID,ClassCourses.MinMark
HAVING ClassCourses.MinMark > sum(StudentMark)
MySQL: Use Aliased Fields in Aggregate Functions
No, you can't use an alias in the select-list or a WHERE clause. You can only use the alias in a GROUP BY, HAVING, or ORDER BY.
You can also use aliases defined in a subquery:
SELECT foo, SUM(foo) AS foo_sum
FROM (
SELECT COUNT(*) AS foo
FROM bar
);
Related Topics
Why Can't I Use an Alias in a Delete Statement
Zero SQL Deadlock by Design - Any Coding Patterns
T-SQL Skip Take Stored Procedure
Select Distinct from Multiple Fields Using SQL
How to Join Two Recordset Created from Two Different Data Source in Excel Vba
T-Sql: Checking for Email Format
How to Change Db Schema to Dbo
Database in Use Error with Entity Framework 4 Code First
Warning: Null Value Is Eliminated by an Aggregate or Other Set Operation in Aqua Data Studio
Select a Column If Other Column Is Null
Postgresql Tables Exists, But Getting "Relation Does Not Exist" When Querying
How to Get the Full Resultset from Ssms
SQL Server Cumulative Sum by Group
Eliminate and Reduce Overlapping Date Ranges
Splitting Comma Separated Values in Columns to Multiple Rows in SQL Server
Using Alias in When Portion of a Case Statement in Oracle SQL