group by后行数比distinct少,主因是null值被强制归为一组及隐式类型转换导致分组合并;需用count(*)与count(col)对比验证,并检查字段排序规则。

GROUP BY后行数比DISTINCT少?先查NULL和隐式类型转换
行数变少,大概率不是SQL“丢数据”,而是你没意识到NULL和类型不一致会强制归组。SQL标准规定所有NULL值在GROUP BY中视为同一组,哪怕它们来自不同记录;如果region或city字段大量为NULL,就会把本该分散的行全压进一组。
隐式类型转换更隐蔽:比如CHAR(10)字段存"北京"(带尾部空格)和VARCHAR(10)存"北京"(无空格),在某些collation下会被判为不同值,分到两组;换种collation又可能被当成相同——结果漂移,行数忽多忽少。
- 用
COUNT(*)、COUNT(col)对比看差异:SELECT region, city, COUNT(*), COUNT(region), COUNT(city) FROM sales GROUP BY region, city - 检查字段真实
collation:SELECT COLUMN_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_NAME = 'sales' AND COLUMN_NAME IN ('region', 'city') - 临时强制统一比较逻辑:
GROUP BY region COLLATE utf8mb4_unicode_ci, city COLLATE utf8mb4_unicode_ci
GROUP BY后行数比预期多?警惕COUNT(DISTINCT)跨组重复计数
这不是分组错了,是统计逻辑本身有歧义。比如按name分组后算COUNT(DISTINCT zhi),再对各组结果SUM(),得到的总数完全可能大于全表COUNT(DISTINCT zhi)——因为同一个zhi值(如zhi = 1)出现在多个name组里,每组都算1次,最后加起来就虚高。
这种场景常见于“每个客户有多少种产品”再汇总成“总共有多少种产品组合”,但业务上真正想问的可能是“全量有多少种独立产品”。二者语义完全不同,不能靠套娃聚合硬凑。
- 确认业务目标:是要“各组内去重后求和”,还是“全局去重”?前者用子查询+
SUM(),后者直接COUNT(DISTINCT zhi) - 避免嵌套误导:
SELECT SUM(tt.he) FROM (SELECT name, COUNT(DISTINCT zhi) AS he FROM tb_fu GROUP BY name) AS tt和SELECT COUNT(DISTINCT zhi) FROM tb_fu本质不是等价替换 - 若真需跨组关联分析,改用
GROUPING SETS或窗口函数,而不是强行SUM聚合结果
MySQL报错或返回随机值?检查ONLY_FULL_GROUP_BY是否开启
MySQL 5.7+默认启用ONLY_FULL_GROUP_BY模式,它会直接拦截“SELECT * + GROUP BY col”这类语义模糊的写法,并报错ERROR 1055;而旧版MySQL或禁用该模式时,会静默返回某一行的任意值(比如销售部的first_name可能是张三也可能是李四),看起来像“丢数据”,其实是未定义行为。
这不是Bug,是SQL标准要求:非聚合列必须明确归属到某组,否则数据库无法决定该取哪条记录的值。
- 查当前模式:
SELECT @@sql_mode,确认是否含ONLY_FULL_GROUP_BY - 安全写法只有两种:
GROUP BY列出所有非聚合字段,或把字段包进聚合函数(如MAX(first_name)) - 别依赖
ANY_VALUE()绕过——它只是掩盖问题,不解决语义歧义
分页总数不准?GROUP BY和COUNT(*)不兼容
框架做分页时调SELECT COUNT(*) FROM (your_group_by_query),结果返回的是分组后的行数(比如12个部门),但前端以为这是原始数据总条数(比如1200条员工记录)。这根本不是SQL的问题,是分页层误把聚合结果当源表了。
尤其在ORM或低代码平台里,自动生成的count语句不会智能识别子查询里有没有GROUP BY,照常执行,导致总数严重偏低。
- 手动校验:
SELECT COUNT(*) FROM your_table和SELECT COUNT(*) FROM (your_group_by_query) t对比数值 - 分页必须拆开:先执行分组主查询,再单独写一条
SELECT COUNT(DISTINCT group_col) FROM your_table获取总组数 - 注意Swoft等老框架的已知缺陷:其
MysqlConnection::receive()方法会错误取第一条结果当总数,需打补丁或升级










