Why is mySQL able to resolve these column aliases

2019-04-26 14:51发布

问题:

Most SQL experts would say one can't re-use an alias in a select at the same level; often to work around this a CTE is used; or one wraps the query as a subquery so the alias can be referenced. However, mySQL seems to allow such a situation provided the alias is referenced in a subquery within the select itself; so it's not technically at the same level.

DEMO:

SELECT CONCAT(a, b) AS c1, CONCAT((SELECT c1), 2)
FROM (SELECT 'a' a, 'b' b, 'c' c UNION ALL 
      SELECT '1', '2', '3') t1;

SELECT 1 a, 2 b, (SELECT A+B) c
     , concat((SELECT a),(SELECT b)) d 
     , greatest((SELECT a),(SELECT b), (SELECT c))

Both of the above queries work.. yes; they work. (or do a really good job of making it seem like they work)

While this does not: as one would expect.

SELECT CONCAT(a, b) AS c1, CONCAT(c1, 2)
FROM (SELECT 'a' a, 'b' b, 'c' c UNION ALL 
      SELECT '1', '2', '3') t1;

So the question here is two fold:

  1. Is this a mySQL "feature" lacking documentation or can someone explain how the compiler is able to resolve the aliases?

  2. Is this a documented feature that one could rely upon; if so where is this documented so one could understand the implications of using this method?

This question stems from one already asked: Select column by alias in MySQL

回答1:

This looks like a parsing bug to me (in other databases, you would get an error on the first two queries).

I can guess at what is happening. MySQL is parsing the subquery (select c1). It doesn't find c1 in the subquery, so it starts looking for references in outer queries.

According to the rules of SQL, it should only be looking at columns in the from clause. However, MySQL also seems to be looking at column aliases.

Although I would call this a bug, MySQL seems to consider it a feature. (Yet another example of MySQL considering a "bug" a "feature".) Consider this query:

SELECT CONCAT((SELECT c1), 2), CONCAT(a, b) AS c1
FROM (SELECT 'a' a, 'b' b, 'c' c UNION ALL 
      SELECT '1', '2', '3') t1;

And the error it generates:

Reference 'c1' not supported (forward reference in item list)

The error suggests that MySQL is intentionally parsing "backwards" references.

For the record, I would never depend on this functionality. It is not only a violation of the ANSI standard and unique behavior among databases. It is confusing. Consider this little modification:

SELECT CONCAT(a, b) AS c1, CONCAT((SELECT c1), 2)
FROM (SELECT 'a' a, 'b' b, 'c' c UNION ALL 
      SELECT '1', '2', '3') t1 CROSS JOIN
     (SELECT 'abcdef' as c1) x;

Which c1 does the query resolve to? I'll let you figure that out. And this doesn't even take into account that c1 could be a variable.



回答2:

  1. Investigation has lead me to this statement: As an extension MySQL also allows them in GROUP BY and HAVING. However, they are invalid in the WHERE clause and in other parts of the SELECT list. For more information see http://dev.mysql.com/doc/refman/5.7/en/problems-with-alias.html. However the section doesn't specifically talk about reuse of alias in select and subquery. The bug does seem to indicate this was a feature in prior versions (5.6.23) but not in 5.7.9..
  2. However further in the above link the below information is provided:

[9 Dec 2015 15:35] Roy Lyseng ...

I read about Aliased expressions (that they can't be used in WHERE clause), but then the question arises:

1. Why is it stopped working? It stopped working because we were screening the MySQL extensions to the SQL standard. And when examining some crashing bugs due to corner cases that were using references to aliased expressions, it was decided that this extension was poorly defined and could be handled by other means. But the crashing bug was using a subquery in the WHERE clause, not in the SELECT list.

2. Why standard SQL was changed? (or it did not previously been given to the standard?) This construction has never been part of standard SQL, it was a MySQL extension to the standard. The standard has never allowed references to aliases, except within the ORDER BY clause.

3. Does exists an option of the configuration that query will working again OR how to find all such requests that doesn't working? We have reconsidered how to handle this problem, and will try to revert the decision on not supporting aliases in subqueries in SELECT list. Thus, we are reopening the bug.

Here is a longer background for the original decision:

Contrary to references to aliases in subqueries in the WHERE clause (and in GROUP BY, for that matter), there is no reason (except standard compliance) that we should not allow references to aliases in the SELECT list, since they should be available in the same phase of query execution. But the support in 5.6 was quite arbitrary:

Given this: create table t1(a int, b int),

Alias in SELECT list is not valid:

  select a+b as c,c+1 from t1;

ERROR 1054 (42S22): Unknown column 'c' in 'field list'

But within a subquery, reference to c is valid:

  select a+b as c,(select c+1) from t1;

And subquery must be after definition of alias:

  select (select c+1),a+b as c from t1;

ERROR 1247 (42S22): Reference 'c' not supported (forward reference in item list)

So, it is easy to say that support for references to aliases in SELECT list was rather ad-hoc. Nevertheless, we will try to reimplement the old solution, but with no attempt at cleaning up the obvious holes in the support for this feature. But referencing aliases in subqueries in the WHERE clause will not be reimplemented.

Conclusions:

  • While it is contrary to common knowledge of referencing an alias in the same select, it does appear mySQL will allow this. It is not common place in any other RDBMS to my knowledge. One needs to consider the order in which the alias is identified (Gordon's answer addresses 3 places one needs to considers).
  • Personally I would gravitate away from this functionality unless additional documentation can be found explaining how it works or is to work. Most developers would stick with the use of a CTE or a subquery to handle the alias reuse and from a maintenance stand point this may be more confusing to others than the standard approaches.
  • "Aliased Expressions" or "references to aliases in the SELECT list" seems to be what this is called. but I can't find much more in documentation of this "feature" (unintended consequence?)


标签: mysql sql alias