SQL Server - 将 varchar 转换为另一个排序规则(代码页)以修复字符编码

Posted

技术标签:

【中文标题】SQL Server - 将 varchar 转换为另一个排序规则(代码页)以修复字符编码【英文标题】:SQL Server - Convert varchar to another collation (code page) to fix character encoding 【发布时间】:2011-12-26 18:08:54 【问题描述】:

我正在查询使用 SQL_Latin1_General_CP850_BIN2 排序规则的 SQL Server 数据库。其中一个表行有一个 varchar,其值包含 +/- 字符(Windows-1252 代码页中的十进制代码 177)。

当我直接在 SQL Server Management Studio 中查询表时,我得到一个乱码字符,而不是该行中的 +/- 字符。当我将此表用作 SSIS 包中的源时,目标表(使用典型的 SQL_Latin1_General_CP1_CI_AS 排序规则)以正确的 +/- 字符结束。

我现在必须构建一种无需 SSIS 即可直接查询源表的机制。我如何以获得正确字符而不是胡言乱语的方式做到这一点?我的猜测是我需要将列转换/转换为 SQL_Latin1_General_CP1_CI_AS 排序规则,但这不起作用,因为我不断收到乱码。

我尝试了以下方法,但没有成功:

select 
columnName collate SQL_Latin1_General_CP1_CI_AS
from tableName

select 
cast (columnName as varchar(100)) collate SQL_Latin1_General_CP1_CI_AS
from tableName

select 
convert (varchar, columnName) collate SQL_Latin1_General_CP1_CI_AS
from tableName

我做错了什么?

【问题讨论】:

欢迎使用 ***:如果您发布代码、XML 或数据示例,在文本编辑器中突出显示这些行并单击“代码示例”按钮 ( ) 在编辑器工具栏上以很好地格式化和语法突出显示它! 【参考方案1】:

字符集转换是在数据库连接级别隐式完成的。您可以使用参数“Auto Translate=False”在 ODBC 或 ADODB 连接字符串中强制关闭自动转换。不建议这样做。 见:https://msdn.microsoft.com/en-us/library/ms130822.aspx

当数据库和客户端代码页不匹配时,SQL Server 2005 中存在代码页不兼容问题。 https://support.microsoft.com/kb/KbView/904803

SQL-Management Console 2008 及更高版本是一个 UNICODE 应用程序。所有输入或请求的值都在应用程序级别被解释。与列排序规则之间的对话是隐式完成的。您可以通过以下方式验证这一点:

SELECT CAST(N'±' as varbinary(10)) AS Result

这将返回0xB100,它是Unicode 字符U+00B1(在管理控制台窗口中输入)。您不能关闭 Management Studio 的“自动翻译”。

如果您在选择中指定不同的排序规则,只要“自动翻译”仍然有效,您最终会进行双重转换(可能会丢失数据)。在选择过程中,原始字符首先转换为新的排序规则,然后将“自动翻译”到“正确的”应用程序代码页。这就是为什么您的各种 COLLATION 测试仍然显示所有相同结果的原因。

如果您将结果转换为VARBINARY 而不是VARCHAR,那么您可以验证指定排序规则确实对选择产生影响,因此客户端不会在 SQL Server 转换出现之前使其无效:

SELECT cast(columnName COLLATE SQL_Latin1_General_CP850_BIN2 as varbinary(10)) from tableName
SELECT cast(columnName COLLATE SQL_Latin1_General_CP1_CI_AS as varbinary(10)) from tableName

如果columnName 仅包含字符“±”,这将分别为您提供0xF10xB1

如果您使用的字体没有提供正确的字形,您仍然可能会得到正确的结果和错误的字符。

请通过在适当的样本上将查询转换为VARBINARY 来仔细检查角色的实际内部表示,并验证此代码是否确实对应于定义的数据库排序规则SQL_Latin1_General_CP850_BIN2

SELECT CAST(columnName as varbinary(10)) from tableName

只要转换始终以相同的方式进出,应用程序排序规则和数据库排序规则的差异可能会被忽视。一旦添加了具有不同排序规则的客户端,就会出现问题。那么你可能会发现内部转换无法正确匹配字符。

综上所述,您应该记住,在解释结果集时,Management Studio 通常不是最终参考。即使它在 MS 中看起来很乱,它仍然可能是正确的输出。问题是记录是否正确显示在您的应用程序中。

【讨论】:

很好的解释!谢谢!【参考方案2】:

必须使用转换,而不是强制转换:

SELECT
 CONVERT(varchar(50), N'æøåáäĺćçčéđńőöřůýţžš')
 COLLATE Cyrillic_General_CI_AI

(http://blog.sqlpositive.com/2010/03/using-convert-with-collate-to-strip-accents-from-unicode-strings/)

【讨论】:

虽然这在理论上可以回答这个问题,it would be preferable 在此处包含答案的基本部分,并提供链接以供参考。【参考方案3】:

我们可能需要更多信息。这是我在 SQL Server 2008 上重现的内容:

CREATE DATABASE [Test] ON  PRIMARY 
    ( 
    NAME = N'Test'
    , FILENAME = N'...Test.mdf' 
    , SIZE = 3072KB 
    , FILEGROWTH = 1024KB 
    )
    LOG ON 
    ( 
    NAME = N'Test_log'
    , FILENAME = N'...Test_log.ldf' 
    , SIZE = 1024KB 
    , FILEGROWTH = 10%
    )
    COLLATE SQL_Latin1_General_CP850_BIN2
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[MyTable]
    (
    [SomeCol] [varchar](50) NULL
    ) ON [PRIMARY]
GO
Insert MyTable( SomeCol )
Select '±' Collate SQL_Latin1_General_CP1_CI_AS
GO
Select SomeCol, SomeCol Collate SQL_Latin1_General_CP1_CI_AS
From MyTable

结果显示原始字符。从 SQL Server 的角度来看,在查询中声明排序规则应该返回正确的字符,但可能是表示层随后会转换为不同的东西,例如 UTF-8。

【讨论】:

【参考方案4】:

尝试:

SELECT CAST( CAST([field] AS VARBINARY) AS varchar) 

【讨论】:

这对我有用。另外我还需要补充一点,程序员应该指出像 (VARBINARY(120)) ... VARCHAR(120) 这样的长度。如果不是,它会削减输出。【参考方案5】:

我认为 SELECT CAST(CAST([field] AS VARBINARY(120)) AS varchar(120)) 为您更新

【讨论】:

以上是关于SQL Server - 将 varchar 转换为另一个排序规则(代码页)以修复字符编码的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server - 将 varchar 转换为另一个排序规则(代码页)以修复字符编码

SQL Server 认为我在使用连字符时尝试将 varchar 转换为 int

在 SQL Server 中将 varchar 转换为 datetime [重复]

SQL Server 2005:将 varchar 值“1.23E-4”转换为十进制失败

如何在 SQL Server 中将 varchar 数据转换为时间

使用自定义格式在 SQL Server 上将 varchar 转换为 datetime