LabHub
学习 学习路径 课程

SQL 实战

聚合 — GROUP BY 实际在做什么

在 LabHub 中继续学习

一句话总结

GROUP BY 把多行折叠成组;折叠后只剩能代表整个组的值,因此 SELECT 列表中只能出现分组键或聚合函数。

概念图: WHERE 在折叠前生效,HAVING 在折叠后生效。 · 忽略 NULL · 聚合前先检查数据符号与缺失值

为什么需要它

“各渠道销售额”或“各状态订单数”这类问题需要的是摘要,而不是单独的行。聚合是把多行折叠为一行的运算,GROUP BY 则定义按什么标准折叠。

它如何工作

记住处理顺序,大多数容易混淆的问题都会迎刃而解。

FROM → WHERE → GROUP BY → 집계 → HAVING → SELECT → ORDER BY → LIMIT

聚合函数会忽略 NULLavg(price) 会把 price 为 NULL 的行也从分母中排除。因此,许多“平均值不对”的问题都来自 NULL 行。如果希望把它视为 0,就必须明确写成 avg(coalesce(price, 0))

掌握 FILTER 子句可以让代码更简洁。它适合在同一个组内按不同条件计算多个聚合值。

SELECT channel,
       count(*) FILTER (WHERE status = 'paid')      AS paid_count,
       count(*) FILTER (WHERE status = 'cancelled') AS cancelled_count
FROM orders
GROUP BY channel;

如果改用 WHERE,就要执行两次查询再进行 join。能否在一次扫描内完成,会直接形成性能差异。

按时间聚合时必须明确时区。如果 ordered_at 是带时区的类型,date_trunc('month', ordered_at) 的结果会随服务器当前时区设置而变化。应像 ordered_at AT TIME ZONE 'Asia/Seoul' 这样固定基准,才能保证无论在哪里执行都得到相同结果。

在实际项目中

种子数据中的退货凭证采用负数数量。如果不加思考地使用 sum(quantity),销量会悄悄减少,热门商品排名也可能颠倒。养成聚合前先检查数据符号与缺失值的习惯,能够避免报表事故。

还有一点:计算平均值时,必须始终追问分母是什么。计算各等级人均销售额时,是否把没有订单的客户计入分母,会让结果产生很大差异;两种答案都可能正确,但必须留下明确定义。

下一篇理论要学什么

下一篇将介绍窗口函数:它不折叠行,却能计算排名与累计值。随后在实验中,会同时求出各状态数量、各渠道销售额、月度汇总和热门商品,并观察负数数量如何改变结果。