group by需配合聚合函数查看分布,如count(*)、avg()等;非聚合字段必须出现在group by中或使用聚合函数;注意null处理、日期函数索引失效、order by字段合法性以及时区对齐。
group by 结果怎么直接看分布?别先写 select *
直接 SELECT * 加 GROUP BY 会报错(MySQL 5.7+ 默认 SQL mode 下),因为非聚合字段没明确归属。你想看“每组有多少条”“每组平均值多少”,得显式写聚合函数。
常见错误现象:ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause
- 必须对
SELECT列中非GROUP BY字段使用聚合函数,比如COUNT(*)、AVG(price)、MAX(created_at) - MySQL 8.0+ 和 PostgreSQL 默认严格模式,不支持“选一个代表值”这种模糊逻辑
- 如果真想看每组的某条原始记录(如最新一条),得用窗口函数或子查询,不是靠
GROUP BY直接解决
用 COUNT / SUM / AVG 看分组统计,但要注意 NULL
COUNT(*) 和 COUNT(字段) 行为完全不同:前者算行数(含 NULL),后者只算该字段非 NULL 的行数。可视化时容易误判“空值占比”。
使用场景:查每个品类销量总和、用户下单频次分布、地区平均响应时长
-
COUNT(*)→ 组内总记录数 -
COUNT(user_id)→ 组内非空 user_id 数(漏掉匿名订单) -
SUM(amount)→ 自动跳过amount为NULL的行,结果不会变 NULL(除非全为 NULL) -
AVG(score)同样忽略 NULL,但若整组 score 全 NULL,结果是NULL,前端展示可能崩
示例:SELECT category, COUNT(*), AVG(price) FROM products GROUP BY category;
想按时间分组(年/月/日)却得到一堆数字?用日期函数对齐粒度
直接 GROUP BY created_at 是按秒分组,根本看不出趋势;而用 YEAR(created_at) 或 DATE(created_at) 才是真正按年、按天聚合。
性能影响:对字段套函数(如 MONTH(created_at))会导致索引失效,大数据量时慢
- 优先考虑生成「日期维度表」或加计算列 + 索引(如 MySQL 5.7+ 支持函数索引:
CREATE INDEX idx_month ON orders ((YEAR(created_at), MONTH(created_at)))) - PostgreSQL 可用
date_trunc('month', created_at),语义更清晰 - 注意时区:数据库时区 vs 应用时区不一致时,
DATE(created_at)可能切错天
GROUP BY 后怎么排序?ORDER BY 必须放在最后,且字段要合法
很多人写 GROUP BY category ORDER BY COUNT(*) DESC 却忘了:排序字段必须出现在 SELECT 列中,或本身就是聚合结果的一部分。否则某些数据库(如 PostgreSQL)直接报错。
容易踩的坑:ORDER BY 里用了未聚合也未 GROUP BY 的字段,例如 ORDER BY name(name 不在 GROUP BY 里,也没聚合)
- 正确写法:
SELECT category, COUNT(*) AS cnt FROM orders GROUP BY category ORDER BY cnt DESC; - MySQL 允许用别名或位置序号(
ORDER BY 2),但其他数据库不一定兼容 - 如果想按组内某个值排序(如“每组最贵商品价格”),得用
MAX(price)而不是price











