一个 GROUP BY 可同时计算多个聚合指标,因优化器将 COUNT(*)、SUM(amount) 等合并为单次分组扫描;非聚合字段须显式写入 GROUP BY;条件统计统一用 CASE WHEN;NULL 值需注意各函数处理差异。
直接在同一个 GROUP BY 查询里写多个聚合函数,就能一次性完成多指标计算——这是标准 SQL 的原生能力,不需要子查询、CTE 或多次扫描。
数据库优化器会把 COUNT(*)、SUM(amount)、AVG(price)、MAX(created_at) 这些聚合表达式合并进同一轮分组过程(比如哈希分组或排序分组)。物理上只读一次表、只做一次分组键匹配,所有指标共享中间结果。
常见错误现象:
– 写了多个 SELECT ... FROM orders GROUP BY region 子查询再 JOIN,导致表被反复扫描
– 在 MySQL 5.7 中对同一派生表引用两次,直接报错 ERROR 1248: Every derived table must have its own alias
region)必须显式出现在 GROUP BY 列表中,否则 PostgreSQL / MySQL 8.0+ 会报 ERROR 1140
COUNT(SUM()) 语法非法)MySQL 允许写 COUNT(status = 'success'),但它实际返回的是行数(因为 status = 'success' 是布尔表达式,在 COUNT 中被转为 1/0,而 COUNT() 统计非 NULL 值),不是你想要的“成功订单数”。语义不清且跨数据库不可移植。
COUNT(CASE WHEN status = 'success' THEN 1 END)
SUM(CASE WHEN ... THEN 1 ELSE 0 END) 虽结果等价,但优化器更难识别其计数意图COUNT(*) FILTER (WHERE status = 'success'),但 MySQL / SQL Server / Oracle 不支持,得回退到 CASE WHEN
比如要同时统计“近7天订单数”和“近30天复购用户数”,不能靠外层加 WHERE 分两次查——那等于两次全表扫描。必须把过滤逻辑下沉到聚合内部。
COUNT(CASE WHEN created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 1 END) AS week_orders
COUNT(*) FILTER (WHERE created_at >= current_date - 7) AS week_orders
SUM(IF(..., 1, 0)):函数语义是求和,不是计数,部分优化器无法做聚合识别优化很多人以为只要写了 GROUP BY 就万事大吉,但真正卡住查询性能或导致报错的,往往是隐式依赖和空值处理。
AVG()、SUM()、MAX() 自动忽略 NULL,但 COUNT(col) 也忽略 NULL,而 COUNT(*) 不忽略——混用时逻辑容易错位amount 都为 NULL,SUM(amount) 返回 NULL,不是 0;需要补 COALESCE(SUM(amount), 0)
GROUP BY a, b 时,若 a 或 b 有 NULL,它们会被当作独立分组值参与聚合,不是“合并到一起”