聚合结果不准源于JOIN字段COLLATION不一致导致隐式转换,使优化器跳过索引、重复扫描或漏分区;须用SHOW FULL COLUMNS查真实Collation,统一ALTER TABLE MODIFY指定CHARACTER SET与COLLATE,外键和UNION字段也需同步对齐。

聚合查询结果不准,不是数据脏,很可能是JOIN字段字符集或COLLATION不一致导致隐式转换,让优化器跳过索引、重复扫描、甚至漏掉分区——统计值自然失真。
查清参与聚合的JOIN字段真实COLLATION
别信表名或字段名“看起来一样”,得看定义:
- 用
SHOW FULL COLUMNS FROM t1 LIKE 'user_id'查Collation列值,注意utf8mb4_general_ci和utf8mb4_0900_as_cs虽同属utf8mb4字符集,但校对规则互不兼容 - 对比
SHOW CREATE TABLE t1和SHOW CREATE TABLE t2,重点看关联字段末尾的CHARACTER SET xxx COLLATE xxx是否完全一致 - 如果聚合涉及
UNION或外键字段(比如FOREIGN KEY (item_id) REFERENCES items(id)),这些字段也必须检查,它们不显式出现在JOIN里,但照样触发字符比对
为什么在GROUP BY或HAVING里加COLLATE没用
GROUP BY t1.name COLLATE utf8mb4_0900_as_cs 这类写法看似能绕过报错,实际会破坏聚合逻辑:
- MySQL 在执行
GROUP BY前必须做分组键归一化,只要原始字段Collation不同,优化器就可能拒绝该表达式,直接报Illegal mix of collations - 即使语法通过,加了
COLLATE的字段无法命中索引,EXPLAIN FORMAT=TREE里会出现type: ALL或Using temporary; Using filesort,导致分组前数据膨胀,COUNT/SUM 结果被放大 -
HAVING中用COLLATE强转字符串做比较(如HAVING MAX(t1.tag) COLLATE utf8mb4_unicode_ci = 'active')同样失效:聚合函数输出的值已脱离原始字段上下文,COLLATE无法反向影响分组过程
ALTER TABLE MODIFY才是唯一可靠解法
临时补丁撑不过一次发布,必须改字段定义本身:
- 先确认目标
COLLATE,例如主表用的是utf8mb4_0900_as_cs,那就全量对齐 - 执行
ALTER TABLE t2 MODIFY item_code VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs—— 必须同时指定CHARACTER SET和COLLATE,只改一个等于白改 - 改完立刻跑
SHOW CREATE TABLE t2确认字段定义末尾出现完整CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs,别只看SHOW FULL COLUMNS,它有时缓存旧值 - 再执行原聚合SQL并
EXPLAIN FORMAT=TREE,重点验证:key是否显示索引名、type是否回到ref或eq_ref、rows是否显著下降
真正容易被忽略的是:聚合误差往往不是单点问题。外键字段、UNION列、甚至全文索引字段只要参与字符串比对,都得同步处理。只修JOIN条件里的字段,报表跑出来还是错的。

















