WHERE 等于条件 returns 映射的 Unicode(全角)结果

WHERE equals condition returns mapped Unicode (fullwidth) results

我们正在查询 SQL 服务器数据库以查找存储在 nvarchar 列中的名称。在这个 table 中,我们有两个相互冲突的值。 WordWord。第一个由全角拉丁字母组成。

当我们尝试select ASCII 名称时,Unicode 版本也returns。这会导致冲突,因为查询应该只能 return 一行。以下是可用于重现结果的查询:

SELECT CASE WHEN N'Word' = N'Word' THEN 1 ELSE 0 END;

此查询 returns 1,而我们期望它 return 0。似乎 SQL 服务器将每个字母的基于 Unicode 的版本映射到它们的 ASCII 变体。

有没有办法禁用 ASCII 和 Unicode 字符之间的这种映射?同时仍然可以忽略大写。

您需要使用 COLLATION。

按照我的示例,找出适合您的排序规则

本次整理returns1

SELECT CASE WHEN N'Word' COLLATE Latin1_General_CI_AS = N'Word' COLLATE Latin1_General_CI_AS THEN 1 ELSE 0 END 

这个排序规则returns0

SELECT CASE WHEN N'Word' COLLATE SQL_Latin1_General_Cp437_BIN = N'Word' COLLATE SQL_Latin1_General_Cp437_BIN THEN 1 ELSE 0 END  

排序规则说明符,告诉SQL服务器如何比较字符。

Find more detail here

List of collations

因为您的数据可能更加多样化,所以我无法判断哪种排序规则最适合您。

When we try to select the ASCII name, the Unicode version also returns.

这种说法有点误解了编码的工作原理。 ASCII 是一种 8 位编码和字符集。它的值是 0 - 127,在大多数代码页和 Unicode 中都很常见。但是,它实际上仅适用于 VARCHAR 数据。当使用 NVARCHAR 时,所有字符都是 Unicode,即使这些字符在其他字符集中也是如此。所以在这里,您只返回 Unicode 字符,因为 NVARCHAR 只包含 Unicode 字符(编码为 UTF-16 Little Endian)。恰好 ASCII 字符集被复制为 Unicode 的子集。

意思是,您在这里真正要说的是您只需要常规的拉丁字符,而不是全角版本。

It seems that SQL Server maps Unicode based versions of each letter to their ASCII variant.

是也不是。 Windows 和 SQL 服务器 可以 将 Unicode 字符映射到 8 位代码页中外观相似的字符,但这仅在将 Unicode 字符串转换为 8 位代码页时发生位代码页(或从一个代码页到另一个代码页)。这不会发生在这里。同样,在这里,您只处理 Unicode。当 Collat​​ion 为 Width Insensitive 时,美国英语字母表的常规形式和全角形式被认为是相等的。并且根据您的问题和测试用例(两个独立的东西,因为在查询列时使用了列的排序规则,但是在仅处理字符串文字 and/or 变量时使用了数据库的默认排序规则),很明显您正在使用的排序规则(可能都是相同的排序规则)是 Width Insensitive.

要解决这个问题,请不要使用二进制排序规则。不幸的是,使用二进制排序规则是 commonly-accepted go-to 解决人们获得比预期更多匹配项时的查询的答案。有时它是正确答案,但通常不是,例如这个问题。

您只需将 "width sensitivity" 添加到您正在使用的排序规则中。您可以使用以下查询找到该列的 Collat​​ion,只需填写正确的 table 名称和列名称:

SELECT col.[collation_name]
FROM   sys.columns col
WHERE  col.[object_id] = OBJECT_ID(N'<schema_name>.<table_name>')
AND    col.[name] = N'<column_name>';

如果排序规则是 Windows 排序规则(即名称 而不是 SQL_ 开头),那么您可以添加 _WS 到排序规则名称的末尾。例如:

Latin1_General_100_CS_AS --> Latin1_General_100_CS_AS_WS

如果排序规则是 SQL 服务器排序规则(即名称 确实 SQL_ 开头),那么 none 允许宽度敏感度,您应该选择等效的 Windows 排序规则。如果排序规则是 SQL_Latin1_General_CP1_*,则尝试以 Latin1_General_100_.

开头的相同操作
-- current Collation (no width sensitivity)
SELECT CASE WHEN N'Word' = N'Word' COLLATE Latin1_General_100_CI_AS THEN 1
            ELSE 0 END;
-- 1


-- add width sensitivity
SELECT CASE WHEN N'Word' = N'Word' COLLATE Latin1_General_100_CI_AS_WS THEN 1
            ELSE 0 END;
-- 0


-- confirm case INsensitivity
SELECT CASE WHEN N'WORD' = N'Word' COLLATE Latin1_General_100_CI_AS_WS THEN 1
            ELSE 0 END;
-- 1

有关为什么在使用二进制排序规则之前应首先尝试获得正确敏感度的更多详细信息,请参阅我的以下 post:

No, Binary Collations are not Case-Sensitive