group by 的核心作用是按指定列分组并聚合计算,select 中非聚合字段必须出现在 group by 中,聚合字段则无需出现;多列分组需一一对应,having 用于过滤分组后结果,where 过滤分组前数据。

GROUP BY 的核心作用就是把数据按指定列“打散成组”,再用 SELECT 拿出每组的代表性信息——不是随便选,而是必须明确:哪些字段是分组依据,哪些字段得靠聚合函数算出来。
SELECT 中字段必须满足分组规则
执行 SELECT category, SUM(amount) FROM sales GROUP BY category 是合法的;但写成 SELECT category, amount, SUM(amount) 就会报错(如 MySQL 的 Expression #2 of SELECT list is not in GROUP BY clause)。这是因为:
- 分组后,
category在每组内值唯一,可以直接选 - 而原始表里的
amount一列在每组里有多个值,数据库不知道该取哪一条——它不帮你“猜”,必须显式用SUM()、MAX()、AVG()等说明怎么压缩 - 这条规则在 MySQL 5.7+(严格模式)、PostgreSQL、SQL Server 都强制执行;只有旧版 MySQL 或 SQLite 可能放行但结果不可靠
多列分组时 SELECT 字段要一一对应
想按部门和职位统计人数,就得写成:
SELECT department, job_title, COUNT(*) FROM employees GROUP BY department, job_title;
注意:GROUP BY 后面的列顺序不影响结果,但 SELECT 中非聚合字段的顺序必须和 GROUP BY 列顺序一致(尤其在配合 ORDER BY 时),否则某些数据库(如 PostgreSQL)会拒绝执行。
- 不能只写
GROUP BY department却在SELECT里加job_title——除非包在MAX(job_title)这类函数里 - 如果业务上需要“每个部门里最常见的职位”,得用窗口函数或子查询,
GROUP BY本身不支持“取组内某条原始记录”
HAVING 必须跟在 GROUP BY 后面过滤分组结果
WHERE 过滤的是“分组前的行”,HAVING 过滤的是“分组后的组”。比如查销售额超 10 万的品类:
SELECT category, SUM(sales) AS total FROM orders GROUP BY category HAVING SUM(sales) > 100000;
-
HAVING SUM(sales) > 100000是合法的,因为SUM(sales)是分组后才有的值 -
HAVING category = 'Electronics'虽然语法通过,但毫无意义——它等价于WHERE,且性能更差 - 别在
HAVING里用未出现在SELECT中的聚合表达式,比如HAVING AVG(price) > 100却没在SELECT里写AVG(price),部分数据库会报错或行为不一致
真正容易被忽略的点在于:GROUP BY 不是“排序工具”,也不是“去重快捷键”。它本质是生成新行集的过程,每一行代表一个组——如果你只想要去重,用 DISTINCT 更直接;如果后续还要对每组做复杂计算(比如中位数、Top N),GROUP BY 往往只是第一步,后面大概率得接子查询或窗口函数。











