SQL Server 存储过程中文乱码主因是排序规则不匹配而非字符集问题,需逐层检查数据库、表列、字符串字面量(须加 N 前缀)、动态 SQL 及客户端驱动是否统一支持 Unicode 或 UTF- 8 排序规则。

SQL Server 存储过程中中文乱码,大概率是排序规则不匹配
不是字符集问题,而是排序规则(Collation)在字符串比较、排序和隐式转换时偷偷改了编码行为。尤其当存储过程里用 NVARCHAR 接收参数,但数据库或列用的是 SQL_Latin1_General_CP1_CI_AS 这类非 Unicode 意识强的排序规则时,CONVERT 或拼接操作可能丢字节。
- 检查当前数据库默认排序规则:
SELECT DATABASEPROPERTYEX('your_db_name', 'Collation') - 检查关键表字段的排序规则:
SELECT name, collation_name FROM sys.columns WHERE object_id = OBJECT_ID('your_table') AND system_type_id IN (167, 175, 231, 239) - 如果返回结果含
_CP1_(如SQL_Latin1_General_CP1_CI_AS),说明它默认按 Code Page 1252 处理,对中文不友好;优先比对是否为Latin1_General_100_CI_AS_SC_UTF8(SQL Server 2019+)或带_SC(Supplementary Characters)标识的规则
ALTER DATABASE …… COLLATE 不会自动更新已有列的排序规则
执行 ALTER DATABASE your_db COLLATE Latin1_General_100_CI_AS_SC_UTF8 只改数据库默认值,不影响已存在表字段的 collation_name。这些字段仍按旧规则解析字符串,导致存储过程里 WHERE name = @input 匹配失败或返回空——你以为是参数没传进来,其实是排序规则不一致导致比较被静默转义。
- 必须逐列修正:用
ALTER TABLE your_table ALTER COLUMN your_col NVARCHAR(100) COLLATE Latin1_General_100_CI_AS_SC_UTF8 - 注意:该操作会重建列,锁表时间取决于数据量;生产环境务必避开高峰
- 若列上有索引、约束或计算列,需先删后建,否则报错
Cannot alter column because it is used in a constraint
存储过程内字符串字面量要加 N 前缀,否则触发隐式转换
写 SET @sql = 'SELECT * FROM t WHERE name ='' 张三 ''',SQL Server 默认按数据库排序规则解释这个字符串字面量。即使列是 NVARCHAR,只要字面量没加 N,就会先转成非 Unicode 编码再比较,中间一步就可能把「张」变成问号或乱码。
- 所有含中文 / 特殊符号的字符串字面量,必须写成
N'张三'、N'ORDER BY 创建时间 DESC' - 动态 SQL 尤其危险:拼接时漏掉任意一个
N,整条语句就退化回 Code Page 模式 - 函数如
REPLACE、SUBSTRING对输入类型敏感——若第一个参数是VARCHAR,第二个参数是NVARCHAR,SQL Server 会把后者转成前者规则,别指望它“聪明”保留 Unicode
SQL Server 2019+ 启用 UTF-8 排序规则前,确认客户端驱动支持
Latin1_General_100_CI_AS_SC_UTF8 看着美好,但 ODBC/JDBC 驱动版本太老时,会把 UTF-8 字节流当成 Latin1 解码,结果还是乱码。不是数据库错了,是中间层没跟上。
- SQL Server Management Studio(SSMS)18.10+ 才完整支持 UTF-8 排序规则显示
- .NET 应用需用 Microsoft.Data.SqlClient 2.1+,旧版
System.Data.SqlClient不识别_UTF8后缀 - Python 的
pyodbc要求驱动为 ODBC Driver 17 for SQL Server + 设置连接字符串UTF8=True
排序规则不是“设一次就完事”的配置项,它是贯穿数据库、表、列、字符串字面量、客户端驱动的链式依赖。改之前先查清哪一层断了,比盲目 ALTER DATABASE 省三天排障时间。