适配ONLY_FULL_GROUP_BY:GROUP BY查询调整问题咨询
核心规则
启用ONLY_FULL_GROUP_BY后,SELECT子句中的每一列必须满足以下两个条件之一:
- 出现在
GROUP BY子句中(作为分组依据) - 被聚合函数(如
COUNT()、SUM()、MAX()等)包裹
关于SELECT *的疑问
是的,不能直接用SELECT *配合GROUP BY。因为SELECT *会返回所有列,其中必然包含不在GROUP BY里的非聚合列,违反ONLY_FULL_GROUP_BY规则,直接触发报错。
逐个示例解答
示例1
原查询:
SELECT headhunter, COUNT(*) AS amount FROM table GROUP BY headhunter
无需调整。amount是COUNT(*)生成的聚合结果,不属于表的原始列,不需要加入GROUP BY。只有表的原始列才需要考虑是否在GROUP BY中。
示例2
原查询:
SELECT *, departure, DATE_FORMAT(departure,'%d.%m.%y') AS departure2 FROM table WHERE carnumber1='' GROUP BY shipid ORDER BY departure DESC
问题说明:
SELECT *包含大量不在GROUP BY(shipid)中的列,违反规则;departure是原始列,既不在GROUP BY里也未被聚合,违规;departure2基于违规的departure生成,同样不符合要求。
修正方案:
- 移除
SELECT *,只保留业务需要的列; - 若需要
departure的值,用聚合函数明确指定取分组内的哪一个值(比如MAX(departure)取最新值); departure2要基于聚合后的departure计算;ORDER BY可直接用聚合后的departure,无需强制加入shipid;- PHP循环用到的列,要么加入
GROUP BY,要么用聚合函数处理后加入SELECT,不需要全部加入ORDER BY(除非排序需要)。
修正后示例(取分组内最新的departure):
SELECT shipid, MAX(departure) AS departure, DATE_FORMAT(MAX(departure),'%d.%m.%y') AS departure2 FROM table WHERE carnumber1='' GROUP BY shipid ORDER BY departure DESC
示例3
原查询:
SELECT customer FROM table WHERE carnumber1!='' GROUP BY customer ORDER BY customer ASC
无需调整。SELECT的列与GROUP BY的列完全一致,符合规则。
示例4
原查询:
SELECT customer FROM table WHERE carnumber1!='' AND preparation>date_sub(NOW(),INTERVAL 6 MONTH) GROUP BY customer ORDER BY customer ASC
无需调整。WHERE子句的过滤条件不影响GROUP BY规则,只要SELECT的列在GROUP BY中即符合要求。
示例5
原查询:
SELECT customer FROM table GROUP BY customer HAVING MAX(preparation)<=date_sub(NOW(), INTERVAL 6 MONTH) ORDER BY customer ASC
无需调整。HAVING中使用MAX(preparation)是合法的聚合操作,preparation不需要加入SELECT或GROUP BY,只要SELECT的列在GROUP BY中即可。
示例6
原查询:
SELECT DATE_FORMAT(delivery,'%M %Y'), COUNT(*) AS amount, SUM(purchase_price) AS summed FROM table GROUP BY MONTH(delivery), YEAR(delivery)
无需调整。DATE_FORMAT(delivery,'%M %Y')与GROUP BY中的MONTH(delivery), YEAR(delivery)逻辑等价(同一分组的delivery年月相同,格式化结果一致),SUM(purchase_price)是聚合函数,完全符合规则。不需要把delivery加入SELECT,也不需要把purchase_price加入GROUP BY。
额外提示
- 若需要分组内某列的特定值(如最新、最早),要明确使用对应聚合函数;
ANY_VALUE()可取分组内任意值,适合不关心具体取值的场景; - 无法开启
ONLY_FULL_GROUP_BY测试时,可手动检查查询中的非聚合列,确保要么在GROUP BY中,要么被聚合函数包裹; - 严格按规则编写查询,避免依赖MySQL旧版本的宽松模式,升级后即可平稳过渡。
内容的提问来源于stack exchange,提问作者WeekendCoder

