MySQL错误1267是因collation不一致触发的硬性报错,必须使参与比较/连接/UNION的列使用完全相同的排序规则;需逐级检查库、表、字段collation并统一,或在SQL中用COLLATE显式指定一致规则。
![thinkphp数据库配置实战:解决“sqlstate[hy000] [1267] illegal mix of collations”的排序规则冲突](https://img.php.cn/upload/article/001/503/042/178254218850650.jpeg)
这个错误不是连接失败,而是 MySQL 在执行 JOIN、WHERE 或 ORDER BY 等操作时,发现参与比较的字段用了不兼容的排序规则(collation),直接拒绝执行。核心问题不在 PHP 或 ThinkPHP 本身,而在于数据库层字符集与排序规则的“混用”。
确认当前数据库、表、字段三级 collation 是否统一
ThinkPHP 报错时只抛出 SQLSTATE[HY000] [1267],但不会告诉你哪两张表、哪个字段在打架。必须手动查:
- 先看数据库默认排序规则:
SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'your_db_name'; - 再查所有表的排序规则:
SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db_name'; - 最后查关键字段(尤其是 JOIN / WHERE 用到的字符串字段):
SHOW FULL COLUMNS FROM your_table_name;关注Collation列
常见冲突组合:一个表是 utf8mb4_unicode_ci,另一个是 utf8mb4_0900_ai_ci;或更老的 utf8_general_ci 和 utf8mb4_unicode_ci 混用。只要任意一级(库/表/字段)不一致,就可能触发该错误。
ThinkPHP 的 database.php 配置必须和实际表结构对齐
很多人改了配置却没效果,是因为只改了连接层,没动数据层。ThinkPHP 的 charset 和 collation 参数只影响客户端连接初始化时的 character_set_client、collation_connection 等会话变量,并不改变已有表的定义。
立即学习“PHP免费学习笔记(深入)”;
- 配置中必须显式指定:
'charset' => 'utf8mb4'和'collation' => 'utf8mb4_unicode_ci'(或你统一选用的其他utf8mb4_*规则) - 如果数据库里存在
utf8(非utf8mb4)表,即使配置写了utf8mb4,MySQL 仍会在隐式转换时失败 - 验证是否生效:在查询前加一句
Db::execute("SHOW VARIABLES LIKE 'collation%';");,确认collation_connection和你配置的一致
批量修正表与字段 collation 的安全操作路径
不能只靠 ALTER TABLE ... CONVERT TO 一锤子修改——它会重写整张表,大表可能锁死、超时,且对已存在的索引字段(如 VARCHAR(255))可能因最大长度限制报错(utf8mb4 占 4 字节,原 utf8 是 3 字节)。
- 优先用
ALTER TABLE table_name DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;——只改表默认值,不影响现有字段 - 再逐字段修正(更稳妥):
ALTER TABLE table_name MODIFY column_name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 若字段有索引,需先
DROP INDEX,改完再ADD INDEX,否则可能报Specified key was too long - 生成批量语句可查:
SELECT CONCAT('ALTER TABLE ', TABLE_NAME, ' MODIFY ', COLUMN_NAME, ' ', DATA_TYPE, IF(CHARACTER_MAXIMUM_LENGTH IS NOT NULL, CONCAT('(', CHARACTER_MAXIMUM_LENGTH, ')'), ''), ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;') FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'your_db_name' AND COLLATION_NAME IN ('utf8_general_ci', 'utf8_unicode_ci');
为什么有时候改完还是报错?重点检查这些地方
最常被忽略的是临时表、视图、函数返回值、甚至 JSON 字段内部的字符串比较。它们可能继承自不同上下文的 collation。
-
JOIN中 ON 条件两侧字段 collation 不同:哪怕两个字段都是VARCHAR,只要 collation 不同,就会触发 1267 - 使用了
CONCAT()、UPPER()等函数后,结果默认继承第一个参数的 collation,可能和另一侧不匹配 - Laravel 或 ThinkPHP 的查询构造器自动生成的临时字段(如
GROUP_CONCAT结果)未显式指定 collation - MySQL 8.0+ 默认
collation_server是utf8mb4_0900_ai_ci,而旧项目习惯用utf8mb4_unicode_ci,二者不兼容,不能混用
真正解决问题,得从数据库设计源头统一 collation,而不是靠每次查询加 COLLATE utf8mb4_unicode_ci 强制转换——那只是掩盖,不是修复。



















