为什么 MySQL 允许 HAVING 使用 SELECT 别名?

Ohl*_*lin 13 mysql database-internals

在SQL中,据我所知,逻辑查询处理顺序,也就是概念解释顺序,以FROM开头的方式如下:

  1. 在哪里
  2. 通过...分组
  3. 选择
  4. 订购者

按照这个列表很容易看出为什么 WHERE 子句中不能有 SELECT 别名,因为别名还没有被创建。T-SQL (SQL Server) 严格遵循这一点,在通过 SELECT 之前不能使用 SELECT 别名。

但是在 MySQL 中,可以在 HAVING 子句中使用 SELECT 别名,即使它应该(逻辑上)在 SELECT 子句之前处理。这怎么可能?

举个例子:

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;
Run Code Online (Sandbox Code Playgroud)

该语句在 T-SQL 中无效(因为 HAVING 指的是 SELECT 别名Amount)...

Msg 207, Level 16, State 1, Line 5
Invalid column name 'Amount'.
Run Code Online (Sandbox Code Playgroud)

...但在 MySQL 中工作得很好。

基于此,我想知道:

  • MySQL 是否在 SQL 规则中走捷径来帮助用户?也许使用某种预分析?
  • 还是 MySQL 使用的概念解释顺序与我虽然所有 RDBMS 都遵循的顺序不同?

pet*_*erm 13

好吧,当您遇到此类问题时,恕我直言,最好的信息来源是 MySQL 文档。现在进入正题。这是GROUP BY默认启用的 MySql 扩展的行为。

MySQL 对 GROUP BY 的扩展
MySQL 扩展了此行为以允许在聚合列的 HAVING 子句中使用别名

如果你想要标准行为,你可以禁用这个扩展 sql_mode ONLY_FULL_GROUP_BY

SET [SESSION | GLOBAL] sql_mode = ONLY_FULL_GROUP_BY;
Run Code Online (Sandbox Code Playgroud)

如果您尝试在ONLY_FULL_GROUP_BYsql_mode 中执行上述查询,您将收到以下错误消息:

HAVING 子句中使用了非分组字段“Amount”:SELECT YEAR(orderdate), COUNT(*) as Amount FROM Orders GROUP BY YEAR(orderdate) HAVING Amount > 1

这是SQLFiddle演示

因此,如何配置和使用 MySQL 实例取决于您。

  • @Pacerier MySQL 正在“进行预分析”,当然,因为查询优化器在选择它认为将是最佳查询计划的同时考虑查询的所有方面。“不同的概念解释”的概念泄露了对服务器可以以任何产生有效结果的方式自由实现概念模型的事实的误解。例如,如果优化器发现最初可以从已经处于所需顺序的索引中按顺序读取行,那么“ORDER BY”实际上可能比理论上的处理时间早得多。 (2认同)

Ray*_*and 5

好问题。

我认为你应该运行这些查询

EXPLAIN SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;
SHOW WARNINGS;
Run Code Online (Sandbox Code Playgroud)

并检查查询是如何重写的。我非常确定查询优化器将 Amount 替换为 COUNT(*)

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING COUNT(*)>1;
Run Code Online (Sandbox Code Playgroud)

就像它一样

select 
 *
from 
 test
where 
 id = 5 - 3
Run Code Online (Sandbox Code Playgroud)

在查询优化器之后,它是这样的。

select 
 test.id as 'id'
from 
 test
where 
 test.id = 2
Run Code Online (Sandbox Code Playgroud)