如何识别SQL Server现有数据编码并修复特殊字符显示异常
问题根源
你的旧数据问题并非NVARCHAR的存储编码问题(NVARCHAR确实默认以UTF-16存储),而是编码转换错误:
- 切换前应用使用ISO-8859-1编码,特殊字符(比如€在ISO-8859-1中是单字节0xA4)被发送到数据库时,SQL Server直接将这个单字节作为UTF-16字符的低字节(高字节补0),存成了U+00A4(通用货币符号,并非真正的欧元符号€)。
- 切换后应用改用UTF-8,€会被正确转换为UTF-16的U+20AC,所以新数据显示正常。旧数据的核心问题是字符码点本身存储错误,和列的排序规则无关。
检查数据编码(码点)的方法
可以通过SQL函数直接查看字符的UTF-16码点,确认错误:
- 用
UNICODE()函数获取字符的UTF-16码点:
SELECT Name, UNICODE(Name) AS CharCodePoint FROM your_table WHERE Name LIKE '%□%' -- 筛选显示为方块的行
- 错误的€会返回
164(即0x00A4),正确的€返回8364(即0x20AC)。
- 用
CONVERT()转成十六进制查看字节:
SELECT Name, CONVERT(VARCHAR(MAX), CAST(Name AS VARBINARY(MAX)), 2) AS HexBytes FROM your_table WHERE Name LIKE '%□%'
- 错误的€对应的十六进制是
A400(UTF-16LE格式),正确的€是AC20。
修复旧数据的步骤
核心是把错误的UTF-16码点(对应ISO-8859-1单字节)重新映射为正确的UTF-16字符,操作前务必先备份表:
- 先测试修复效果,避免误改:
SELECT Name, CONVERT(NVARCHAR(MAX), CONVERT(VARBINARY(MAX), Name), 28591) AS FixedName FROM your_table WHERE Name LIKE '%□%' -- 或筛选UNICODE码点在128-255之间的行
这里28591是ISO-8859-1的代码页,该语句会把存储的UTF-16字节(实际是ISO-8859-1单字节补0)重新按ISO-8859-1解析,转换为正确的UTF-16字符。
2. 确认测试结果正确后,执行更新:
UPDATE your_table SET Name = CONVERT(NVARCHAR(MAX), CONVERT(VARBINARY(MAX), Name), 28591), Value = CONVERT(NVARCHAR(MAX), CONVERT(VARBINARY(MAX), Value), 28591) WHERE UNICODE(Name) BETWEEN 128 AND 255 -- 只处理可能是ISO-8859-1错误转换的字符
为什么之前的方法无效
- 修改排序规则:仅影响字符的比较、排序逻辑,不会改变存储的字符码点,因此无法修复错误的字符显示。
- 转成VARCHAR UTF8:旧数据的码点本身是错误的,转换后只是把错误的码点转成UTF8格式,依然无法纠正编码映射错误。
内容的提问来源于stack exchange,提问作者Mr P
相关产品推荐
相关产品推荐

