group by必须位于where之后、order by之前,且select中所有非聚合字段都须出现在group by列表中;count(*)统计所有行(含null),count(字段)仅统计该字段非null值;having用于过滤分组后的结果,where用于分组前过滤。

GROUP BY 用错位置,统计结果就全乱了
GROUP BY 必须出现在 WHERE 之后、ORDER BY 之前,且所有非聚合字段都得出现在 GROUP BY 列表里。常见错误是漏写某个 SELECT 中的普通字段,比如:
SELECT user_id, COUNT(*) FROM orders WHERE status = 'paid' GROUP BY user_id; ✅
SELECT user_id, created_at, COUNT(*) FROM orders GROUP BY user_id; ❌ —— created_at 没进 GROUP BY,多数数据库直接报错:ERROR: column "created_at" must appear in the GROUP BY clause
- MySQL 5.7+ 默认严格模式下也会报错,别依赖旧版“隐式分组”行为
- 如果真要查每个用户的最早下单时间,得明确写成
MIN(created_at),而不是裸列 -
GROUP BY的字段顺序不影响结果,但会影响排序稳定性(尤其没加ORDER BY时)
COUNT(*) 和 COUNT(字段) 差得远,别默认等价
COUNT(*) 统计行数,包括 NULL;COUNT(字段) 只统计该字段非 NULL 的行。这个差异在处理缺失数据时特别关键。
比如用户表里有 100 条记录,其中 12 条 email 是 NULL:
SELECT COUNT(*) FROM users; → 返回 100
SELECT COUNT(email) FROM users; → 返回 88
- 想统计“有邮箱的用户数”,必须用
COUNT(email),不是COUNT(*) -
COUNT(1)和COUNT(*)效果一样,但语义上*更清晰,别为了“看起来快”硬写1 - 聚合前加
WHERE过滤比在COUNT里套CASE更直观,除非要一并算多个条件
SUM 和 AVG 遇到 NULL 就跳过,但别以为它们会自动过滤掉错误值
SUM 和 AVG 会忽略 NULL,但不会忽略非法类型或溢出。比如对字符串字段用 SUM(price),PostgreSQL 直接报错:ERROR: function sum(text) does not exist;MySQL 可能静默转成 0,导致结果失真。
- 确保被聚合的列是数值类型,用
pg_typeof()(PostgreSQL)或 DESCRIBE(MySQL)先确认
- 金额类字段优先用
DECIMAL,别用 FLOAT——AVG 算出来可能是 99.99999999999999
- 需要排除异常值(比如价格 > 100000)?先
WHERE price BETWEEN 0 AND 100000,别指望 AVG 自己识别“离谱”
HAVING 不是 WHERE 的替代品,它是唯一能筛聚合结果的出口
WHERE 在分组前过滤行,HAVING 在分组后过滤组。想查“订单数超 5 的用户”,必须用 HAVING COUNT(*) > 5,写在 WHERE 里会报错:ERROR: aggregate functions are not allowed in WHERE。
-
HAVING 只能用在含 GROUP BY 的查询里,单行聚合(如 SELECT COUNT(*))不需要它
- 可以同时存在
WHERE 和 HAVING:前者减小分组数据量,后者控制最终输出组数
- 别在
HAVING 里重复写 WHERE 已做过的事,比如 WHERE status = 'paid' 后又在 HAVING 里写 HAVING SUM(amount) > 0——逻辑冗余还拖慢性能
聚合本身不难,难的是搞清每一步数据还在不在、有没有被隐式丢弃、类型还对不对。特别是跨数据库迁移时,MySQL 的宽松模式和 PostgreSQL 的严格校验会让同样一条 COUNT 语句表现完全不同。
pg_typeof()(PostgreSQL)或 DESCRIBE(MySQL)先确认DECIMAL,别用 FLOAT——AVG 算出来可能是 99.99999999999999WHERE price BETWEEN 0 AND 100000,别指望 AVG 自己识别“离谱”WHERE 在分组前过滤行,HAVING 在分组后过滤组。想查“订单数超 5 的用户”,必须用 HAVING COUNT(*) > 5,写在 WHERE 里会报错:ERROR: aggregate functions are not allowed in WHERE。
-
HAVING只能用在含GROUP BY的查询里,单行聚合(如SELECT COUNT(*))不需要它 - 可以同时存在
WHERE和HAVING:前者减小分组数据量,后者控制最终输出组数 - 别在
HAVING里重复写WHERE已做过的事,比如WHERE status = 'paid'后又在HAVING里写HAVING SUM(amount) > 0——逻辑冗余还拖慢性能
COUNT 语句表现完全不同。










