必须统一character_set_client、character_set_connection和character_set_results为utf8mb4,否则group_concat等聚合函数返回中文会乱码或问号,因聚合过程继承connection字符集而非字段定义。

必须统一 character_set_client、character_set_connection 和 character_set_results 三者为 utf8mb4,否则任何聚合函数(如 GROUP_CONCAT、MAX、ANY_VALUE)返回的中文都可能乱码或变问号。
为什么 GROUP BY 或 GROUP_CONCAT 后中文变乱码
不是字段本身坏了,而是聚合过程在错误字符集上下文中执行:MySQL 的 GROUP_CONCAT 不读字段定义的字符集,只继承当前 session 的 character_set_connection;GROUP BY 后取非聚合字段(如 SELECT name, COUNT(*) FROM t GROUP BY dept_id)时,若 sql_mode 含 ONLY_FULL_GROUP_BY,MySQL 会拒绝执行;若关闭该模式,它会“随机”挑一行的 name 值返回——而这个值若已在连接层被错误解码(比如用 latin1 解了 utf8mb4 字节),那后续所有聚合结果都基于乱码字符串,再怎么 CONVERT 都救不回来。
常见错误现象:
-
GROUP_CONCAT(name)返回????或沪A12345类乱码 - 单条
SELECT name FROM t LIMIT 1正常,一加GROUP BY就乱 -
SHOW VARIABLES LIKE 'character_set%'中至少一项是latin1或utf8(非utf8mb4)
如何验证并修复连接层字符集
先确认当前会话实际使用的字符集,而不是依赖建库或驱动默认值:
- 执行
SHOW VARIABLES LIKE 'character\_set%';,重点看character_set_client、character_set_connection、character_set_results三项是否全为utf8mb4 - 如果不是,临时修复:在查询前加
SET NAMES utf8mb4;(等价于同时设 client/connection/results) - 更可靠的做法是在连接初始化时固定:MySQLi 连接时指定
charset='utf8mb4',JDBC URL 加characterEncoding=utf8mb4,pymysql 连接参数写charset='utf8mb4' - 避免只改
character_set_database或表级COLLATE——它们不影响聚合计算时的编码上下文
GROUP_CONCAT 中文被截断或排序错乱
GROUP_CONCAT 默认按字节长度截断(group_concat_max_len = 1024),且排序规则受连接层 collation_connection 影响。UTF-8mb4 下一个中文占 3–4 字节,1024 字节最多拼 256 个左右汉字,超出部分直接丢弃。
- 查当前限制:
SHOW VARIABLES LIKE 'group_concat_max_len'; - 临时调高:
SET SESSION group_concat_max_len = 2000000;(单位是字节,不是字符) - 中文排序失效?检查
collation_connection是否为utf8mb4_unicode_ci或utf8mb4_general_ci;用GROUP_CONCAT(name ORDER BY name COLLATE utf8mb4_unicode_ci)显式指定 - 别对已拼好的结果做
CONVERT(... USING utf8mb4)——拼接时就错了,转换只是给乱码再套一层壳
pymssql 连 GBK 数据库时的特殊处理
当 SQL Server 数据库存的是 GBK 编码(常见于老 ERP/一卡通系统),而你用 pymssql 以 charset='utf8' 连接,返回的中文会是 沪A12345 这类乱码——本质是 Python 把 GBK 字节流当 UTF-8 解了。此时不能简单改 charset='gbk',因为 pymssql 内部对非中文字段兼容性差。
- 保持连接用
charset='utf8' - 对每个字符串字段做中转修复:
text.encode('latin-1').decode('GBK') - 封装成工具函数,避免手动判断字段类型:
def fix_gbk_text(text): if not isinstance(text, str): return text try: return text.encode('latin-1').decode('GBK') except (UnicodeEncodeError, UnicodeDecodeError): return text - 注意:这个修复只适用于数据库**确实存的是 GBK 字节**的场景;如果字段本身是
nvarchar+N'中文'写入的,则无需此步骤
最易被忽略的一点:字符集问题从来不是“改一个地方就好”,而是客户端、连接、服务端、存储层四者必须咬合。哪怕 SHOW CREATE TABLE 看起来全是 utf8mb4,只要连接时 character_set_client 是 latin1,SQL 语句里的中文字符串字面量(比如 WHERE name = '张三')就会被错误解析,后续所有逻辑都建立在错误输入上。











