GROUP BY后SELECT列必须是分组字段或聚合函数,否则报错;多维分组需列出所有字段;WHERE过滤行、HAVING过滤分组;COUNT(*)统计所有行,COUNT(col)仅统计非NULL值。

GROUP BY 后 SELECT 列必须是分组字段或聚合函数
直接写 SELECT category, name, COUNT(*) FROM products GROUP BY category 会报错,比如 MySQL 报 Expression #2 of SELECT list is not in GROUP BY clause。这是因为 SQL 标准要求:SELECT 中非聚合的列(如 name)必须出现在 GROUP BY 中,否则数据库无法确定该取哪一行的 name 值。
常见做法是只选分组字段 + 聚合结果:
SELECT category, COUNT(*) AS cnt FROM products GROUP BY category;
如果真要附带某个代表性的 name(比如每个类别的第一个商品),得用子查询或窗口函数,不能靠裸 GROUP BY 硬凑。
统计「多个类别维度」要用多字段 GROUP BY
比如既要按 category,又要按 status 分组统计,就得把两个字段都写进 GROUP BY,顺序无关,但缺一不可:
SELECT category, status, COUNT(*) AS cnt FROM orders GROUP BY category, status;
- 结果会是「每个 category-status 组合」一行,不是先按 category 汇总、再拆 status
- 如果想同时看「大类总数」和「子类明细」,得用
GROUPING SETS(PostgreSQL/SQL Server)或WITH ROLLUP(MySQL),但兼容性差,别默认指望 - 注意 NULL 值:如果
category或status有 NULL,它们会被当作一个独立分组参与统计
WHERE 和 HAVING 的分工不能颠倒
WHERE 过滤行,HAVING 过滤分组——这是最容易混淆的点。比如「只统计订单数超过 5 的类别」,必须用 HAVING:
SELECT category, COUNT(*) AS cnt FROM orders WHERE created_at >= '2024-01-01' -- 先筛日期范围内的订单 GROUP BY category HAVING COUNT(*) > 5; -- 再筛分组后的数量
-
WHERE COUNT(*) > 5是语法错误,因为COUNT(*)在WHERE阶段还没算出来 -
HAVING可以用聚合函数,也能用分组字段(如HAVING category != 'test'),但没实际意义——分组字段本身就在SELECT里了 - 性能上,
WHERE越早过滤掉无用行,GROUP BY处理的数据越少,所以条件尽量往前放
COUNT(*)、COUNT(1)、COUNT(col) 行为差异很关键
三者看起来都“数行”,但语义不同:
-
COUNT(*):统计组内所有行数,包括col为 NULL 的行 -
COUNT(1):和COUNT(*)行为一致,只是常量表达式,多数引擎会优化成同一逻辑 -
COUNT(price):只统计price非 NULL 的行数——这点常被忽略,导致统计值偏小
例如查「每个品类有多少商品有标价」,才该用 COUNT(price);查「每个品类上架了多少商品」,必须用 COUNT(*)。
另外,COUNT 不会去重,要算不同值个数得用 COUNT(DISTINCT col),但代价高,慎用在大表上。










