group by本质是折叠行,将相同分组键的多行归并为一行,导致结果行数减少;窗口函数则是追加列,保持原始行数不变。

GROUP BY 的本质就是折叠行 —— 它不是“筛选”,而是“归并”。结果变小不是 bug,是设计使然。
GROUP BY 为什么天然减少行数
SQL 执行时,GROUP BY 会扫描所有满足 WHERE 条件的原始行,再按分组键值把“值相同”的行聚成一组,每组只保留一行输出。这意味着:
- 原始表有 1000 行,但
shop_id只有 12 个不同值 →GROUP BY shop_id最多返回 12 行 - 哪怕某组内有 200 行,也只出 1 行;没聚合函数的非分组字段(如
user_name)取值不确定,MySQL 默认取第一行(5.7+ 开启ONLY_FULL_GROUP_BY会报错) - 空值(
NULL)在绝大多数数据库中被当作同一分组键处理:100 行shop_id IS NULL→ 合并为 1 行
哪些操作会让 GROUP BY “意外”更少
行数比预期还少,往往不是分组逻辑错了,而是前置条件或数据本身干扰了分组基数:
-
WHERE条件写太严:比如WHERE status = 'approved'把本该参与分组的草稿、待审数据全过滤掉了 - 关联用
INNER JOIN:右表无匹配时整行丢弃,导致左表某些分组键根本没机会进入分组阶段 - 分组字段含隐式截断或空格:例如
VARCHAR(5)存'abc '和'abc',入库时可能都被存为'abc',分组时撞车 -
HAVING过滤过猛:比如HAVING COUNT(*) > 1000,直接干掉小分组,而你其实想看全部分组分布
如何快速验证是不是分组导致的“消失”
别猜,直接比对原始分组键的唯一性与实际分组数:
- 查真实唯一值:
SELECT COUNT(DISTINCT shop_id) FROM t_baoxiao WHERE ... - 查分组后行数:
SELECT COUNT(*) FROM (SELECT shop_id FROM t_baoxiao WHERE ... GROUP BY shop_id) t - 如果两者不等,说明问题出在
WHERE或关联逻辑上;如果相等但业务仍觉得“少了”,大概率是NULL合并或类型隐式转换在起作用
GROUP_CONCAT 后数据“变少”其实是假象
看起来 GROUP_CONCAT 让结果少了,其实是你忘了它必须配合 GROUP BY 使用。单独写 GROUP_CONCAT(img_path) 而没 GROUP BY,MySQL 会把全表当 1 组处理,返回 1 行 —— 这不是数据丢了,是语义上你就只要 1 行聚合结果。
真正要注意的是:一旦加了 GROUP BY,GROUP_CONCAT 就只 concat 当前组内的值;如果某组没关联上图片(LEFT JOIN 后 i.img_path 为 NULL),那这组的 img_info 就是空字符串,不是 NULL,也不报错,容易误以为“数据缺失”。
最常被忽略的一点:分组字段的 NULL 处理是数据库行为,不是 SQL 标准强制要求,MySQL、PostgreSQL、Oracle 都默认合并 NULL,但有些 OLAP 引擎(如 Doris、StarRocks)可配置是否开启此行为 —— 换环境时得重验。










