GROUP BY 是分组汇总的唯一合理路径,必须包含所有非聚合字段,配合 WHERE(行过滤)和 HAVING(组过滤)正确分工,并注意索引、临时表及 COUNT(*) 优化。

GROUP BY 配合聚合函数是汇总查询的唯一合理路径
直接在 SELECT 中写 COUNT()、SUM()、AVG() 而不加 GROUP BY,只会返回单行结果——这适合统计全表,但无法按维度分组汇总。真要“按部门算人数”“按日期算销售额”,GROUP BY 不可省略,且必须包含所有非聚合字段。
常见错误:SELECT dept, name, COUNT(*) FROM emp GROUP BY dept 会报错(MySQL 5.7+ 严格模式下),因为 name 既没参与分组也没被聚合。正确写法是只选分组键和聚合表达式:SELECT dept, COUNT(*) AS cnt FROM emp GROUP BY dept。
- MySQL 8.0+ 支持
GROUP BY后用序号引用 SELECT 列(如GROUP BY 1),但可读性差,不建议在生产中使用 - 聚合字段别名不能在
GROUP BY中直接引用(如GROUP BY cnt报错),必须写原始表达式或列位置 - 含
GROUP BY的查询默认隐式排序,但 MySQL 8.0+ 已取消该行为,需显式加ORDER BY才能保证顺序
WHERE 和 HAVING 的分工必须分清
WHERE 过滤的是「行」,在分组前生效;HAVING 过滤的是「组」,只能用聚合结果做条件。想查“订单数超 100 的客户”,必须用 HAVING COUNT(*) > 100,写在 WHERE 里会报错或逻辑错误。
典型误用:SELECT user_id, COUNT(*) FROM orders WHERE COUNT(*) > 100 GROUP BY user_id —— COUNT(*) 在 WHERE 阶段还不存在,直接语法错误。
-
WHERE可用索引加速,HAVING无法走索引,性能更敏感,应尽量把能前置的条件挪到WHERE - 时间范围筛选(如
created_at >= '2024-01-01')务必放在WHERE,否则全表分组后再过滤,浪费大量计算 -
HAVING中支持复杂表达式,比如HAVING AVG(price) * COUNT(*) > 5000,但要注意字段作用域仅限于当前分组结果
避免在聚合查询中 SELECT 大字段或 JOIN 冗余表
一旦用了 GROUP BY,MySQL 必须为每组暂存中间结果。如果 SELECT 中包含 TEXT、BLOB 字段,或 JOIN 了大表却只取其中几个字段,会显著放大临时表体积,甚至触发磁盘临时表(Created_tmp_disk_tables 增多)。
例如:统计每个用户的最新订单时间,错误写法是 SELECT u.name, o.* FROM users u JOIN orders o ON u.id = o.user_id GROUP BY u.id ORDER BY o.created_at DESC —— 这不仅语义错误(o.* 无法确定取哪一行),还会加载全部订单字段。
- 优先用子查询或窗口函数(MySQL 8.0+)替代多表聚合:比如用
(SELECT MAX(created_at) FROM orders o2 WHERE o2.user_id = u.id)替代 JOIN - 确认
GROUP BY字段上有索引,尤其是复合索引要匹配分组顺序(如GROUP BY status, date对应索引(status, date)) - 用
EXPLAIN FORMAT=TREE(8.0+)或EXPLAIN观察是否出现Using temporary; Using filesort,这是性能瓶颈信号
COUNT(*) 和 COUNT(字段) 的实际差异远不止“是否忽略 NULL”
COUNT(*) 统计行数,InnoDB 可利用二级索引最小叶子节点快速估算(某些场景甚至不扫数据页);而 COUNT(字段) 必须检查该字段是否为 NULL,即使字段有索引,也大概率要回表或扫描完整索引。
更隐蔽的问题:当字段允许 NULL 且业务逻辑依赖精确非空计数时,COUNT(col) 是对的;但若只是要“记录总数”,用 COUNT(*) 不仅语义清晰,还能触发优化器更激进的执行策略。
- MyISAM 表上
COUNT(*)是 O(1),但 InnoDB 没有全局行数缓存,仍需遍历索引 -
COUNT(1)和COUNT(*)在 MySQL 中完全等价,无需刻意替换 - 对大表做
COUNT(*)时,若允许误差,可查information_schema.TABLES中的TABLE_ROWS(仅近似值,且 MyISAM 准确、InnoDB 不可靠)
聚合查询的性能拐点往往不在函数本身,而在分组键的选择、索引覆盖程度和中间结果集大小。一个没加索引的 GROUP BY 字段,比写错十个聚合函数影响都大。










