问题描述

最近在做一道SQL题目的时候,突然产生了一个这样的疑问:

  1. 按照SQL执行顺序的话,应该是这样执行的:
from
join
on
where
group by
having
select
distinct
order by
limit
  1. 基于第一条,那么order by可以使用select中定义的别名,这个是合理的。
  2. 但是在实践中发现,如下边的那条SQL语句,having子句也可以使用select中定义的别名refund_rate ,按道理来讲,执行引擎执行到having时应该报找不到refund_rate的定义,这让我感觉有点想不通。
select 
    product_id,
    round(sum(if_click) / count(product_id), 3) ctr,
    round(IF(sum(if_click) = 0, 0, sum(if_cart) / sum(if_click)), 3) cart_rate,
    round(IF(sum(if_cart) = 0, 0, sum(if_payment) / sum(if_cart)), 3) payment_rate,
    round(IF(sum(if_payment) = 0, 0, sum(if_refund) / sum(if_payment)), 3) refund_rate
from
    tb_user_event
where
    year(event_time) = 2021 and month(event_time) = 10
group by
    product_id
having 
    refund_rate <= 0.5
order by
    product_id
;

问题解决

通常这种问题的解决,不外乎两种渠道:

  1. StackOverflow等论坛
  2. MySQL官网

渠道一:StackOverflow论坛

通过搜索之后,发现也有跟我一样有此疑惑的老外:

问题链接:https://stackoverflow.com/questions/49888360/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.
只有MySQL数据库运行在having子句中用别名,这里并没有按照标准的SQL

换句话说,标准的SQL标准是不允许在having中使用select子句中的别名,但是MySQL对这个地方进行了扩展

渠道二:MySQL8.0官网

链接: https://dev.mysql.com/doc/refman/8.0/en/problems-with-alias.html

An alias can be used in a query select list to give a column a different name. You can use the alias in GROUP BY, ORDER BY, or HAVING clauses to refer to the column:

官网中明确表示

  1. 可以在group byorder byhaving子句中使用别名。**
  2. 不可以在where中使用别名。

这是因为可能会出现列值还未确定的情况:
SELECT id, COUNT(*) AS cnt FROM tbl_name WHERE cnt > 0 GROUP BY id;

另外,MySQL8.0官网的这篇文章也详细说明了这一部分。

补充:

在MySQL5.7中,可以对sql_mode属性添加ONLY_FULL_GROUP_BY值来禁止此扩展。在MySQL8.0中此方法无效。

若禁用了之后,类似的语句在执行的过程中就会报错:
Non-grouping field 'XXXXX' is used in HAVING clause: ........... 测试链接:http://sqlfiddle.com/#!9/98dbf7/1

总结

以后在MySQL中,可以大胆的用:

  1. 可以在group byorder byhaving子句中使用别名。**
  2. 不可以在where中使用别名。

其他数据库不一定支持此操作。

参考