在 MySQL 多表关联查询时,经常会遇到类似这样的错误:
(1267, "Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='")
这是由于不同表或字段使用了不一致的字符集排序规则导致的冲突。本文提供一套完整的解决方案,帮助你将整个数据库的排序规则统一,彻底解决此类问题。
MySQL 中,字符集(如 utf8mb4)定义了如何存储字符,而排序规则(如 utf8mb4_0900_ai_ci)定义了字符的比较和排序方式。当执行 JOIN、WHERE 等操作时,如果涉及的字段排序规则不一致,就会触发此错误。
我们将通过以下步骤解决问题:
重要提示:在执行任何结构修改前,务必备份整个数据库!
sqlmysqldump -u username -p --databases luck > luck_backup.sql
替换 username 为你的数据库用户名,luck 为你的数据库名。
首先查看数据库、表和字段的当前排序规则,以便了解问题所在:
sql-- 查看数据库默认排序规则
SELECT DEFAULT_COLLATION_NAME
FROM INFORMATION_SCHEMA.SCHEMATA
WHERE SCHEMA_NAME = 'luck';
-- 查看所有表的排序规则
SELECT TABLE_NAME, TABLE_COLLATION
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'luck';
-- 查看所有字段的排序规则(重点关注外键关联字段)
SELECT COLUMN_NAME, COLLATION_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'luck';
在修改表结构前,临时禁用外键约束检查:
sqlSET FOREIGN_KEY_CHECKS = 0;
将整个数据库的默认排序规则设置为 utf8mb4_0900_ai_ci(MySQL 8.0 默认):
sqlALTER DATABASE luck CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
生成修改所有表的 SQL 语句:
sqlSELECT CONCAT('ALTER TABLE `', TABLE_NAME, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;')
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'luck';
执行上述查询后,会输出类似以下的多条 ALTER TABLE 语句:
sqlALTER TABLE `table1` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
ALTER TABLE `table2` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
...
复制这些语句并逐个执行。
生成修改所有字段的 SQL 语句:
sqlSELECT CONCAT(
'ALTER TABLE `', TABLE_NAME, '` MODIFY `', COLUMN_NAME, '` ',
COLUMN_TYPE,
' CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci',
IF(IS_NULLABLE = 'NO', ' NOT NULL', ''),
IF(COLUMN_DEFAULT IS NOT NULL, CONCAT(' DEFAULT ''', COLUMN_DEFAULT, ''''), ''),
';')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'luck'
AND COLLATION_NAME IS NOT NULL
AND COLLATION_NAME != 'utf8mb4_0900_ai_ci';
执行上述查询后,会输出类似以下的多条 ALTER TABLE 语句:
sqlALTER TABLE `users` MODIFY `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL;
ALTER TABLE `products` MODIFY `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
...
复制这些语句并逐个执行。
如果修改外键关联字段时遇到错误(如 3780 - 字段不兼容),需按以下步骤处理:
如果 table1.id 被 table2.ref_id 引用,先修改 table2.ref_id:
sqlALTER TABLE `table2` MODIFY `ref_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL;
ALTER TABLE `table1` MODIFY `id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL;
sql-- 查看外键约束名
SHOW CREATE TABLE table2;
-- 删除外键约束
ALTER TABLE table2 DROP FOREIGN KEY fk_table2_ref_id;
-- 修改字段
ALTER TABLE `table1` MODIFY `id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL;
ALTER TABLE `table2` MODIFY `ref_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL;
-- 重新添加外键约束
ALTER TABLE table2 ADD CONSTRAINT fk_table2_ref_id FOREIGN KEY (`ref_id`) REFERENCES `table1` (`id`);
完成所有修改后,恢复外键约束检查:
sqlSET FOREIGN_KEY_CHECKS = 1;
确认所有表和字段的排序规则已统一:
sqlSELECT TABLE_NAME, COLUMN_NAME, COLLATION_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'luck'
AND COLLATION_NAME != 'utf8mb4_0900_ai_ci';
如果查询结果为空,则表示所有字段已成功统一。
如果你不想手动执行上述步骤,可以使用以下 Python 脚本自动完成:
pythonimport pymysql
db_config = {
'host': 'localhost',
'user': 'your_username',
'password': 'your_password',
'database': 'luck',
'charset': 'utf8mb4'
}
try:
conn = pymysql.connect(**db_config)
cursor = conn.cursor()
# 禁用外键约束
cursor.execute("SET FOREIGN_KEY_CHECKS = 0;")
print("已禁用外键约束检查")
# 修改数据库排序规则
cursor.execute("ALTER DATABASE luck CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;")
print("已修改数据库默认排序规则")
# 修改所有表的排序规则
cursor.execute("""
SELECT CONCAT('ALTER TABLE `', TABLE_NAME, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;')
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'luck';
""")
alter_table_queries = [row[0] for row in cursor.fetchall()]
for query in alter_table_queries:
cursor.execute(query)
print(f"执行: {query}")
# 修改所有字段的排序规则
cursor.execute("""
SELECT CONCAT(
'ALTER TABLE `', TABLE_NAME, '` MODIFY `', COLUMN_NAME, '` ',
COLUMN_TYPE,
' CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci',
IF(IS_NULLABLE = 'NO', ' NOT NULL', ''),
IF(COLUMN_DEFAULT IS NOT NULL, CONCAT(' DEFAULT ''', COLUMN_DEFAULT, ''''), ''),
';')
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'luck'
AND COLLATION_NAME IS NOT NULL
AND COLLATION_NAME != 'utf8mb4_0900_ai_ci';
""")
alter_column_queries = [row[0] for row in cursor.fetchall()]
for query in alter_column_queries:
cursor.execute(query)
print(f"执行: {query}")
# 恢复外键约束
cursor.execute("SET FOREIGN_KEY_CHECKS = 1;")
print("已恢复外键约束检查")
# 提交事务
conn.commit()
print("所有表和字段的排序规则已统一!")
except Exception as e:
print(f"错误: {e}")
conn.rollback()
finally:
cursor.close()
conn.close()
备份的重要性:操作前一定要备份数据库,防止数据丢失
锁表问题:大表的 ALTER TABLE 操作可能会导致长时间锁表,建议在业务低峰期执行
版本兼容性:utf8mb4_0900_ai_ci 是 MySQL 8.0+ 的默认排序规则,如果使用 MySQL 5.7,建议使用 utf8mb4_unicode_ci
应用代码检查:修改后需确认应用代码中的 SQL 查询和比较逻辑仍然正常工作
性能影响:统一排序规则可能会影响部分查询的性能,建议在修改后进行性能测试
Mysql数据库表排序规则批量统一解决 https://www.bilibili.com/opus/1067757616035266560
本文作者:周得水
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!