SQL 分组统计有哪几种写法?三种用法一次讲清

编程狮(w3cschool.cn) 2026-10-01 07:05:48 浏览数 (36)
反馈

SQL 分组统计三种写法一次讲清

你有一张订单表,老板却问“每个客户的消费总额是多少”,你盯着数据发愣:SQL 分组统计到底有哪几种写法?答案是三条主线:基础的分组计数、分组后过滤的 HAVING、以及多列分组加多聚合。今天这篇文章,编程狮就从最小示例讲到边界,把这三种写法连同最容易错的地方一次讲清。读完你能直接套用在报表、对账和数据看板上,不再现写现查。

SQL 的统计能力,大多围绕 GROUP BY 展开。分组统计的本质是“先按某一列把行归类,再对每一类做一次聚合”。很多初学者卡住,不是不会写,而是分不清 WHERE 和 HAVING 谁先谁后、分组列和聚合列能不能混用。下面先把结论摆出来,再逐写法拆解。

先看结论

SQL 分组统计本质是按列归类再聚合,三种写法覆盖绝大多数报表需求:

你的需求 写法 一句话理由
按一列统计每组的量 GROUP BY + COUNT 最基础,人人都要会
只保留满足统计条件的组 GROUP BY + HAVING 在分组之后过滤,不是之前
按多列交叉统计 GROUP BY a, b + 多聚合 一次算出多维报表

SQL 分组统计三种写法对应场景

如果你还没熟悉 SQL 基础,建议先过一遍 SQL 教程,把 SELECT 和聚合函数铺平,后面读代码会更顺。

一、写法一:最小可运行示例(按一列分组计数)

最基础的 SQL 分组统计,就是把某一列相同的行归为一组,再用 COUNT 数每组有多少行。这种分组查询写法简单,却是日常报表的基石。下面用一张 orders 表演示:它有三列 id、customer、amount。

SELECT customer, COUNT(*) AS orders
FROM orders
GROUP BY customer;

这条语句做了三件事:从 orders 取数、按 customer 列把行归组、对每个组数出订单数。GROUP BY customer 决定了“以谁分组”,COUNT(*) 决定“每组算什么”。运行后你会得到每个客户一行,以及他对应的订单数;如果表为空,结果是零行而不是报错,这是正常预期。

验证要点:在 MySQL 或任意支持 SQL 的环境建一张小表插入几行,执行上面语句,预期按客户聚合并打印每组的订单数;若报 customer 不在 GROUP BY 里,说明你 SELECT 了非聚合列却没把它加进分组。无论你用 Node.js 教程 在后端调数据库,还是直接在客户端跑,这条语法都通用。

二、写法二:分组后带条件过滤(HAVING)

需求升级:只想要“订单数大于 3”的客户。很多人本能地写 WHERE COUNT(*) > 3,这是错的——WHERE 在分组之前过滤行,那时还没有“每组的订单数”这个概念。HAVING 才是分组之后对组做过滤的语法。

SELECT customer, COUNT(*) AS orders
FROM orders
GROUP BY customer
HAVING COUNT(*) > 3;

上面语句先分组、再对组应用条件 HAVING COUNT(*) > 3,只返回订单数超过 3 的客户。记住顺序:WHERE 筛行、GROUP BY 分组、HAVING 筛组。这三者的执行次序是 SQL 分组统计里最容易翻车的地方,写错一步结果就完全不对。

边界说明:HAVING 里可以引用聚合结果(如 COUNT(*)),但 WHERE 里不行;想同时用两者,把它们各放在正确的位置上即可。聚合函数如 SUM(amount) 也能放进 HAVING,用来筛“消费总额达标的客户”。

三、写法三:多列分组与多聚合

再升级:既要按客户、又要按月份交叉统计,并且同时算出订单数和总金额。这就用到多列分组和多个聚合函数一起上。

SELECT customer, DATE_FORMAT(order_date, '%Y-%m') AS month,
       COUNT(*) AS orders, SUM(amount) AS total
FROM orders
GROUP BY customer, DATE_FORMAT(order_date, '%Y-%m');

这条语句按“客户 + 月份”两列组合分组,对每个组合同时算出 orders 和 total。多列分组时,只有两列都相同的行才归为同一组,从而得到一张二维交叉表。运行后每行代表“某客户在某月”的订单数与金额;缺数据的月份自然不会有行,这是预期行为,不是 bug。

拓展:聚合函数不止 COUNT 和 SUM,AVG 算均值、MAX/MIN 取极值也常一起用。SQL 统计的灵活性,很大程度来自这些聚合函数与 GROUP BY 的自由组合。想看更全的聚合清单,可翻 MySQL 官方文档的对应章节。

四、常见误区与边界

误区一:“WHERE 和 HAVING 差不多。” 差很多,WHERE 在分组前筛行、HAVING 在分组后筛组,顺序写反结果就错。误区二:“SELECT 里能随便写列。” 非聚合列必须出现在 GROUP BY 里,否则多数数据库直接报错。误区三:“分组会按我想要的顺序排。” 分组不保证顺序,要排序必须显式加 ORDER BY。

边界上:NULL 在分组里会被归为“同一组”(一个 NULL 组);GROUP BY 和聚合搭配时,索引设计直接影响大表查询速度,数据量大时要给分组列建索引。SQL 统计 最基础的动作就是分组,但性能好坏往往取决于你有没有为分组列建对索引。

实操清单:前置、操作、验证与失败处理

  • 前置(安装/配置):本机装好 MySQL 8.0 或任意支持标准 SQL 的数据库,并能用客户端连上。
  • 操作(命令/运行):建表 orders 并插入几行测试数据,把上面的示例逐条执行。
  • 验证(检查/预期):预期第一条返回每客户订单数,第二条只返回超过 3 单的客户,第三条返回客户×月份交叉统计。
  • 失败处理(失败/修复):若报“不在 GROUP BY”,把 SELECT 的非聚合列补进 GROUP BY;若 HAVING 用不了聚合,确认它放在 GROUP BY 之后。

SQL 分组统计三要点

总结

SQL 分组统计有哪几种写法?核心是三条:基础 GROUP BY + COUNT 做单列分组计数、GROUP BY + HAVING 在分组后过滤、多列 GROUP BY 配合多聚合函数做交叉报表。SQL 分组统计的关键在于分清 WHERE 筛行与 HAVING 筛组的先后,以及非聚合列必须进 GROUP BY。把这三件事记牢,绝大多数报表需求都能直接套。

要点带走:

  • 分组统计 = 先归类、再聚合,GROUP BY 决定“按谁分”;
  • WHERE 在分组前筛行,HAVING 在分组后筛组,顺序不能反;
  • SELECT 的非聚合列必须写进 GROUP BY,否则报错;
  • 大表分组要给分组列建索引,否则查询会明显变慢。

下一步建议把窗口函数(如 OVER (PARTITION BY ...))补上,理解了分组统计,你就能平滑过渡到更复杂的分析查询。

延伸学习

想把这块知识系统补齐,可以按这个顺序来:

  1. 先过一遍 SQL 入门课程,把聚合与分组基础铺平;
  2. 方言细节记不住时,翻 MySQL 教程 查具体语法;
  3. 想从学习路径切入,这篇 怎么学习 SQL 语句 讲得更细,值得延伸阅读。

常见问题

Q:WHERE 和 HAVING 到底差在哪?

A:WHERE 在分组之前过滤“行”,此时聚合结果还不存在,所以不能用 COUNT 这类聚合;HAVING 在分组之后过滤“组”,可以引用聚合结果。要筛“每组的订单数大于 3”,只能用 HAVING,不能用 WHERE。

Q:SELECT 里写了没分组的列会怎样?

A:在标准 SQL 里,SELECT 的非聚合列必须出现在 GROUP BY 中,否则数据库直接报错,提示该列不在分组里。少数数据库对此宽松,但结果不可靠,建议严格把非聚合列都写进 GROUP BY。

Q:分组之后怎么让结果按数量排序?

A:分组不保证顺序,需要显式加 ORDER BY。例如 ORDER BY COUNT(*) DESC 能让订单最多的客户排在最前;想按金额排就 ORDER BY SUM(amount) DESC,排序要写在 HAVING 之后、语句末尾。

0 人点赞