中文乱码主因是连接层字符集未对齐,需确保character_set_client、connection、results均为utf8mb4;GROUP_CONCAT乱码优先排查字符集而非长度;校对规则影响分组逻辑,推荐统一用utf8mb4_unicode_ci。

GROUP BY 或 GROUP_CONCAT 后中文变问号、 Mojibake(如“æäºº”),不是 SQL 写错了,而是连接层字符集没对齐——哪怕表字段是 utf8mb4,只要 character_set_client 或 character_set_connection 是 latin1 或 utf8(MySQL 的阉割版),乱码就必然发生。
查当前连接实际生效的字符集
别只看建库语句或 SHOW VARIABLES LIKE 'character_set_database',GROUP BY 和 GROUP_CONCAT 用的是 session 级连接参数。执行:
SHOW VARIABLES LIKE 'character_set%';
重点关注这三项是否全为 utf8mb4:
character_set_clientcharacter_set_connectioncharacter_set_results
任一不是 utf8mb4,聚合结果就可能出问题。常见陷阱:Navicat 默认连接用 utf8(实为 utf8mb3),不支持 emoji 和部分生僻汉字;命令行 mysql -u root -p 不加参数时默认用 latin1。
连接初始化时必须显式声明 utf8mb4
应用代码里漏掉 charset 声明,等于白配数据库。不同场景写法不同:
- Python +
pymysql:charset='utf8mb4'必须传进pymysql.connect(),不能只靠SET NAMES utf8mb4 - JDBC URL:
useUnicode=true&characterEncoding=utf8mb4(注意是&,不是&;且必须是utf8mb4,不是utf8) - PHP + PDO:
mysql:host=localhost;charset=utf8mb4要写在 DSN 里,set_charset()是补救,不是替代 - SQL Server +
pymssql:若后端存的是 GBK,反而不能设charset='gbk',得用charset='utf8'+ 后处理(见下一条)
遇到 GBK 存储的老系统,别硬改连接 charset
有些 ERP、物流系统用 SQL Server + GBK 存中文,pymssql 设 charset='gbk' 容易崩,设 charset='utf8' 又显示成 沪A12345。这时要绕开连接层,做字节中转:
def fix_gbk_text(text):
if isinstance(text, str):
try:
return text.encode('latin-1').decode('GBK')
except (UnicodeEncodeError, UnicodeDecodeError):
pass
return text
原理是:pymssql 用 utf8 解了 GBK 字节 → 得到错误 Unicode 字符串 → 用 latin-1 无损转回原始字节 → 再用 GBK 正确解码。这个逻辑必须在取数后、展示前执行,不能指望 SQL 层修复。
GROUP_CONCAT 截断或乱码,光调长度不够
GROUP_CONCAT 乱码常被误认为是 group_concat_max_len 太小,其实优先级更高的是字符集。即使你把长度设到 1000000,只要连接字符集不是 utf8mb4,拼接过程已损坏,后续 CONVERT(... USING utf8mb4) 也救不回来。
- 先确保
character_set_client等三者是utf8mb4 - 再查
SHOW VARIABLES LIKE 'group_concat_max_len';,单位是字节,不是字符;含 emoji 时按 4 字节/字符预留空间 - 字段本身字符集混乱?用
GROUP_CONCAT(CAST(name AS CHAR CHARACTER SET utf8mb4))强制转码,比CONVERT更可靠
真正容易被忽略的点是:校对规则(collation)影响分组行为本身。比如字段用 utf8mb4_bin,中文大小写、全半角、简繁体都算不同值,GROUP BY 会多分几组,视觉上像“乱码”——但其实是分组逻辑变了。查字段 collation 用 SHOW FULL COLUMNS FROM t LIKE 'name';,推荐统一用 utf8mb4_unicode_ci。











