数据本身未损坏,乱码源于SUBSTR等函数在latin1连接字符集下错误解析UTF-8字节;三者character_set_client/connection/results必须全为utf8mb4,且函数中所有USING和CAST均须显式指定utf8mb4。

SELECT HEX() 显示合法 UTF-8 字节但函数结果乱码
这说明数据本身没坏,是 SUBSTR、REPLACE、CONCAT 等函数在执行时用了错误的字符集上下文。MySQL 会按 character_set_connection 解析字符串字面量,并按该字符集做内部运算——如果它是 latin1,哪怕字段存的是 utf8mb4,REPLACE('你好', '好', '棒') 也会被当成 4 个 latin1 字节处理,切分错位、替换失效。
验证方式:运行 SELECT @@character_set_client, @@character_set_connection, @@character_set_results; ——三者必须全为 utf8mb4。常见陷阱是只改了 character_set_server,却没碰连接层。
- 临时修复:在执行含中文的字符串函数前,先运行
SET NAMES utf8mb4; - 脚本执行时,第一行必须是
SET NAMES utf8mb4;,不能依赖配置文件自动生效 - 避免在存储过程中硬写中文字符串(如
SET @msg = '成功';),除非确认创建过程前已设好连接字符集
函数返回值被截断或显示为问号
CONVERT、CAST 或隐式转换触发乱码,往往是因为目标字符集不兼容。例如字段定义为 VARCHAR(255) CHARACTER SET utf8mb4,但函数里写 CONVERT(col USING utf8),就会把 4 字节 emoji 当作非法序列丢弃,变成 ?。
根本原因:MySQL 的 utf8 是别名,实际为 utf8mb3,不支持 emoji;而 utf8mb4 才是完整 UTF-8。任何显式或隐式使用 utf8 的转换都可能出问题。
- 检查函数中所有
USING子句,替换成utf8mb4,例如:CONVERT(col USING utf8mb4) - 避免用
CAST(col AS CHAR),它默认走character_set_connection;应写成CAST(col AS CHAR CHARACTER SET utf8mb4) - ORM 或应用层调用函数时,确保连接参数带
charset=utf8mb4,否则驱动可能绕过服务端设置
ORDER BY / GROUP BY 中文排序异常或结果不稳定
这不是乱码,但常被误认为是字符集问题——实际是排序规则(collation)不匹配导致的。比如字段用 utf8mb4_0900_as_cs,但 ORDER BY name 却按 utf8mb4_unicode_ci 比较,大小写敏感性、重音处理逻辑不同,结果顺序就飘。
更隐蔽的问题:主从库 collation 不一致时,从库 GROUP BY 可能合并错误的行,因为比较逻辑变了,但错误不报错,只悄悄出错。
- 查字段真实 collation:
SHOW FULL COLUMNS FROM t1 LIKE 'name';,确认Collation列是utf8mb4_0900_as_cs或utf8mb4_unicode_ci,且主从一致 - 函数内强制指定 collation:
ORDER BY name COLLATE utf8mb4_0900_as_cs - 避免在函数里混用不同 collation 的字段,例如
CONCAT(a, b)中 a 和 b collation 不同,结果会退化为utf8mb4_bin,排序完全失效
mysqldump 导出后函数逻辑失效
导出的 SQL 文件里,如果建表语句没带 CHARACTER SET utf8mb4,或者函数体里的中文字符串被 mysqldump 用 latin1 读出来再写进去,那恢复后 CREATE FUNCTION f() RETURNS VARCHAR(100) BEGIN RETURN '测试'; END 里的 '测试' 已经是损坏字节,函数一执行就返回乱码。
关键点:mysqldump 默认按 character_set_client 读数据,不是按表定义读——所以即使表是 utf8mb4,若 dump 时连接是 latin1,函数体就废了。
- 导出必须加
--default-character-set=utf8mb4,且放在命令最前面 - 打开 dump 文件,搜
CREATE FUNCTION,确认里面中文是否可读;不可读就说明 dump 时已损毁,重导 - 导入时同样要加
--default-character-set=utf8mb4,否则客户端用latin1解析函数体,二次损坏 - 函数创建前必须
SET NAMES utf8mb4,否则 MySQL 解析函数定义时就错解中文
mysql.proc 表。一旦创建时错了,后续所有调用都错,且无法通过改配置修复。


















