存储过程中文乱码本质是客户端、连接、库、表、列五层字符集不统一所致,需重点检查character_set_client、connection、results三个会话变量,确保全链路使用utf8mb4并显式声明字符集。

存储过程里中文乱码,先查数据库和连接的字符集是否一致
乱码通常不是存储过程本身的问题,而是客户端、连接、库、表、列这五层字符集不统一导致的。重点盯住 character_set_client、character_set_connection 和 character_set_results 这三个会话级变量,它们决定 SQL 语句怎么被解析、怎么被计算、结果怎么返回。
执行这条命令看当前连接的实际配置:
SHOW VARIABLES LIKE 'character\_set%';
如果其中任意一个值是 latin1 或 utf8(注意不是 utf8mb4),就极可能出问题。特别是 MySQL 5.7+ 默认用 utf8mb4,但很多旧应用仍靠驱动或连接字符串硬设成 utf8,结果插入四字节 emoji 或某些生僻中文时截断或报错。
- 连接字符串里必须显式指定
characterEncoding=utf8mb4(JDBC)、charset=utf8mb4(Python pymysql)等参数,不能依赖服务端默认 - 建库存储过程前,确保库已用
CREATE DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;创建 -
SET NAMES utf8mb4;要在调用存储过程前执行,否则即使库是 utf8mb4,当前会话也可能走默认 latin1
存储过程中拼接中文字符串,避免隐式转换丢失内容
MySQL 在拼接不同字符集的字符串时会尝试转换,但规则复杂且容易失败。比如把一个 utf8mb4 列和一个未声明字符集的字符串字面量(如 '用户不存在')拼在一起,MySQL 可能按连接的 character_set_client 解析该字面量,导致实际存入的是乱码字节。
安全做法是显式标注字符串字符集:
CONCAT(_utf8mb4'用户ID:', user_id, _utf8mb4' 不存在');
注意前缀 _utf8mb4 是字符集 introducer,不是函数,不能加括号;它强制该字符串按 utf8mb4 解析,不受会话变量影响。
- 所有硬编码中文都加
_utf8mb4前缀,包括IF判断里的字符串、INSERT的 VALUES 部分、RETURN值 - 不要依赖
CAST('xxx' AS CHAR),它不指定字符集,转换行为不确定 - 如果过程里要处理来自不同表的字段,先用
CONVERT(col_name USING utf8mb4)统一转码再拼接
从存储过程返回中文结果集,确保结果集元数据字符集正确
即使过程内部逻辑全用 utf8mb4,如果结果集字段没明确字符集,客户端仍可能按错误编码解码。典型表现是:SELECT 出来字段值看着正常,但用 SELECT ... INTO OUTFILE 或 JDBC ResultSet.getString() 拿到的就是乱码。
关键在定义结果集结构时绑定字符集。例如用游标或临时表构造结果集:
CREATE TEMPORARY TABLE tmp_result ( msg VARCHAR(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci );
或者在 SELECT 返回前,用 CONVERT 显式转码:
SELECT CONVERT(msg USING utf8mb4) AS msg FROM some_table;
- 临时表、结果表、变量声明中所有涉及中文的
VARCHAR/TEXT类型,必须带CHARACTER SET utf8mb4 - 过程内定义的局部变量(如
DECLARE v_msg VARCHAR(100);)默认继承数据库字符集,但不可靠,建议显式声明:DECLARE v_msg VARCHAR(100) CHARACTER SET utf8mb4; - 如果用
SELECT ... INTO @var赋值,@var 的字符集由查询结果决定,务必确保源字段或表达式已带 utf8mb4 标识
排查存储过程字符集问题,优先检查这三个地方
很多问题卡在“看起来没问题”,其实某个环节悄悄退化了。最常被忽略的是连接初始化阶段和过程调用链中的隐式上下文切换。
- 检查应用代码里建立连接后是否立刻执行了
SET NAMES utf8mb4—— 很多 ORM 会覆盖这个设置,得在每次获取连接后重置 - 查看存储过程定义时的
CREATE PROCEDURE语句是否含CHARACTER SET utf8mb4子句(MySQL 8.0+ 支持,但 5.7 不支持,别白找) - 用
SHOW CREATE PROCEDURE proc_name;看实际保存的定义,确认里面所有中文字符串是否仍是可读状态;如果已经变成问号或乱码,说明创建时连接就不对,过程体本身已损坏
字符集问题从来不是单点故障,而是整条链路上某处松动。修复时别只改存储过程,得同步校准连接、库、表、字段、客户端五层配置,缺一不可。

















