group by本质是行折叠而非筛选,将相同分组键的多行归并为一行,导致结果行数减少;例如5000行中shop_id仅12个不同值,则最多返回12行,null值也被视为同一分组键。

GROUP BY 本质是行折叠,不是筛选
结果变少不是出错,是 GROUP BY 的设计行为。它把所有分组键值相同的行合并成一行,每组只输出一次。比如 t_baoxiao 表有 5000 条记录,但 shop_id 只有 12 个不同值,那 GROUP BY shop_id 最多返回 12 行——哪怕某组含 800 行,也只出 1 行。
常见误解是“数据丢了”,其实只是被归并了。真正要查原始明细,得去掉 GROUP BY 或改用窗口函数(如 ROW_NUMBER() OVER (PARTITION BY shop_id))。
- NULL 值默认被当作同一分组键:所有
shop_id IS NULL的行会压进 1 行 - 隐式类型转换也会“撞组”:比如
VARCHAR字段存'123 '和'123',入库时可能都被截成'123',分组时就合并了 - MySQL 5.7+ 开启
ONLY_FULL_GROUP_BY后,SELECT shop_id, user_name FROM t GROUP BY shop_id会直接报错,因为user_name没聚合也没出现在GROUP BY中
为什么加了 GROUP_CONCAT 就只剩 1 行?
GROUP_CONCAT 必须配合 GROUP BY 才有意义。如果漏写 GROUP BY,MySQL 会把整张表当 1 组处理,返回 1 行聚合结果——这不是数据消失,是你没告诉数据库“按什么分组”。
在你贴的 SQL 里,GROUP_CONCAT(...) 跟着一堆 a.* 字段,但没写 GROUP BY a.id(或其他唯一字段),就会触发这个行为。
- 正确写法示例:
SELECT a.id, GROUP_CONCAT(i.img_path) FROM t_baoxiao a LEFT JOIN t_img i ON i.type_id = a.id WHERE ... GROUP BY a.id -
LEFT JOIN后某报销单没图片,GROUP_CONCAT返回空字符串'',不是NULL,容易误判为“数据缺失” - 若想保留所有报销单且显示是否有图,应搭配
COUNT(i.id) > 0 AS has_img这类显式判断,别只靠GROUP_CONCAT输出是否为空
LEFT JOIN + GROUP BY 为什么“丢”了左表某些行?
LEFT JOIN 保证左表每行都出现,但 GROUP BY 若只按左表字段分组,所有右表匹配失败的行会被压缩进同一个“空组”,导致维度坍缩。例如按 a.shop_id 分组,而某 shop_id 下所有 t_img 都没匹配上,它不会单独成行,而是和其它“无图”的 shop_id 合并——除非你把右表字段(如 i.id)也放进 GROUP BY 或用于判空。
- 检查方式:执行
SELECT COUNT(*) FROM t_baoxiao WHERE delmark = 1 AND ...得到原始行数,再对比GROUP BY后行数 - 若两者相等,说明没丢行,只是分组逻辑让你误以为少了
- 若不等,重点查
WHERE条件是否过严(比如漏掉了delmark = 0的草稿)、INNER JOIN是否误用、或JOIN条件里i.del_flag = 1把有效图片过滤掉了
怎么快速验证是不是 GROUP BY 导致“变少”?
别猜,直接比对分组键的唯一性与实际分组数:
- 查真实唯一值:
SELECT COUNT(DISTINCT shop_id) FROM t_baoxiao WHERE delmark = 1 AND shop_id != 0 - 查分组后行数:
SELECT COUNT(*) FROM (SELECT shop_id FROM t_baoxiao WHERE delmark = 1 AND shop_id != 0 GROUP BY shop_id) t - 如果两个数字不等,问题出在
WHERE或JOIN阶段;如果相等但业务仍觉得“少了”,大概率是NULL合并或字段类型/空格问题
最常被忽略的是:分组字段含空格、大小写混用、或跨表 JOIN 时字段类型不一致(比如左表 INT,右表 VARCHAR),这些都会让本该分开的行悄悄合并。











