根本原因是character_set_client、connection、results未统一为utf8mb4,导致create procedure时中文字面量被错误解析并固化损坏;必须在创建前执行set names utf8mb4,并显式声明变量character set utf8mb4。

存储过程里中文显示为问号或方块,不是代码写错了,而是变量声明时没绑定字符集,导致 MySQL 把 utf8mb4 字节当 latin1 解析了。
CREATE PROCEDURE 里中文字符串字面量被错误解析
你写 SELECT '你好' 或 SET @msg = '测试',MySQL 不会自动按 utf8mb4 解这些字符串——它完全依赖当前会话的 character_set_client。如果该值是 latin1(旧客户端默认),那两个汉字会被当作 4 个 latin1 字节存进 mysql.proc 表,永久损坏。
- 现象:执行后返回
??、,或SHOW CREATE PROCEDURE里中文注释已乱码 - 关键点:
character_set_client决定“你怎么发”,character_set_connection决定“我怎么转”,character_set_results决定“我怎么回”——三者必须全为utf8mb4 - 不要在建过程前只执行
SET character_set_client = utf8mb4,它不生效;必须用SET NAMES utf8mb4同时覆盖三者 - 批量建过程脚本第一行必须是
SET NAMES utf8mb4;,不能靠 my.cnf 或连接参数兜底
存储过程内变量未显式指定 CHARSET
声明变量如 DECLARE msg VARCHAR(100); 时,MySQL 默认继承数据库字符集,但若数据库是 latin1 或 utf8(非 utf8mb4),变量内部存储就会丢字节。尤其当你把该变量用于拼接 SQL 或返回结果时,乱码直接暴露。
- 修复方式:显式加
CHARACTER SET utf8mb4,例如DECLARE msg VARCHAR(100) CHARACTER SET utf8mb4; - 函数参数同理:
IN p_name VARCHAR(50) CHARACTER SET utf8mb4比裸写更安全 - 临时表字段也要注意:
CREATE TEMPORARY TABLE tmp (txt VARCHAR(100) CHARACTER SET utf8mb4) - 别用
utf8——MySQL 的utf8是阉割版,不支持 emoji 和大部分生僻字
客户端连接未强制 utf8mb4 导致过程调用失效
即使存储过程体本身没问题,调用它的客户端(比如 PHP 的 mysqli、Python 的 pymysql)若没声明 charset,仍会以 latin1 连接,导致过程内 SELECT 返回的结果被客户端用错编码解码。
- PHP mysqli:连接后立即执行
$mysqli->query("SET NAMES utf8mb4"),或初始化时传['charset' => 'utf8mb4'] - Python PyMySQL:
connect(..., charset='utf8mb4')必须显式传,不能省略 - 命令行 mysql 客户端:
mysql -u root -p --default-character-set=utf8mb4启动,再进库 - JDBC URL 加参数:
?useUnicode=true&characterEncoding=utf8mb4,&不能写成&
最容易被忽略的是:存储过程创建后,mysql.proc 表里的 body 字段已经固化了错误编码,改完配置也救不回来——必须删掉重创,且重建前确保 SET NAMES utf8mb4 已生效。











