基于SQL执行顺序:GROUP BY为何能使用SELECT中CASE生成的列?
你提到的SQL执行顺序是标准逻辑顺序,但实际不同数据库在GROUP BY的处理上有扩展实现,而WHERE严格遵循标准顺序,这就是两者差异的核心原因。
WHERE不能用SELECT别名的原因
标准SQL的执行顺序里,WHERE是第2步(在FROM之后),而SELECT是第5步。当执行WHERE的时候,SELECT语句里定义的列别名(比如你的Performance)还没被计算生成——数据库此时只能识别原始表中的字段(比如COM_SALES_PRC),根本不知道Performance对应的是什么逻辑,所以直接用别名会报错。
拿你的报错查询来说,你想用WHERE Performance IS NOT NULL过滤数据,但此时Performance这个CASE表达式的结果还没生成,数据库无法解析这个别名,自然抛出错误。
GROUP BY能用SELECT别名的原因
标准SQL其实也不允许GROUP BY使用SELECT的别名,但像MySQL这类数据库,在未开启ONLY_FULL_GROUP_BY模式时做了扩展优化:它会提前解析SELECT中的表达式,把别名和对应的逻辑关联起来,执行GROUP BY时自动将别名替换成对应的CASE表达式。
你的无报错查询本质上等价于:
SELECT CASE WHEN COM_SALES_PRC > 1000000 THEN "good job" END AS Performance, COUNT(CUST_SEQ_NO) AS NumberofEmployees FROM PREP_MONTHLY_STAT GROUP BY CASE WHEN COM_SALES_PRC > 1000000 THEN "good job" END
但要注意:如果开启了ONLY_FULL_GROUP_BY(这是MySQL 5.7+的默认模式),这个查询也会报错,因为此时MySQL会严格遵循标准SQL,GROUP BY只能使用FROM子句中的原始字段或聚合函数。
报错查询的修正方法
要实现你的需求,有两种常见方式:
方法1:直接使用原始字段条件
既然WHERE只能识别原始表字段,直接写COM_SALES_PRC > 1000000即可,和Performance IS NOT NULL等价:
SELECT CASE WHEN COM_SALES_PRC > 1000000 THEN "good job" END AS Performance, COUNT(CUST_SEQ_NO) AS NumberofEmployees FROM PREP_MONTHLY_STAT WHERE COM_SALES_PRC > 1000000 GROUP BY Performance
方法2:用子查询提前生成别名
先通过子查询计算出Performance,外层查询就可以用这个别名过滤了:
SELECT Performance, COUNT(CUST_SEQ_NO) AS NumberofEmployees FROM ( SELECT CASE WHEN COM_SALES_PRC > 1000000 THEN "good job" END AS Performance, CUST_SEQ_NO FROM PREP_MONTHLY_STAT ) t WHERE Performance IS NOT NULL GROUP BY Performance
内容的提问来源于stack exchange,提问作者jay

