N'?c' 使用 Latin1_General_CI_AS 排序规则认为 N'C' 的重复键

Yar*_*lav 11 sql-server collation t-sql sql-server-2014 unique-constraint

我有一个带有唯一键的表,其中包含一NVARCHAR(50)列(正确与否,但在那里)。因此,当尝试插入?cC(与插入顺序无关)时,由于整理问题,它会在第二次插入时中断。这是错误:

(受影响的 1 行)消息 2601,级别 14,状态 1,第 16 行无法在具有唯一索引“IX_TestT”的对象“dbo.testT”中插入重复的关键行。重复的键值为 (C)。

选择退货:

在此处输入图片说明

数据库默认排序规则是Latin1_General_CI_AS. 花了一些时间寻找如何解决它,而不会对现有结构进行太多更改,但找不到开始工作的方法。尝试了不同的排序规则和组合,一切都失败了。阅读(这里这里)关于字符扩展等,仍然卡住了。这是我用来复制问题的示例代码,请随时修改并推荐任何有助于解决此问题的内容。

CREATE TABLE testT (
    [Default_Collation]     [NVARCHAR] (50) COLLATE DATABASE_DEFAULT,
    [Latin1_General_CI_AS]  [NVARCHAR] (50) COLLATE Latin1_General_CI_AS,
    [Latin1_General_CI_AI]  [NVARCHAR] (50) COLLATE Latin1_General_CI_AI,
    [SQL_Collation]         [NVARCHAR] (50) COLLATE SQL_Latin1_General_CP1_CI_AS);
CREATE UNIQUE CLUSTERED INDEX [IX_TestT] ON [dbo].[testT] ([Default_Collation])
ON [PRIMARY]
GO

INSERT INTO testT
SELECT  N'?c',  --COLLATE Latin1_General_CI_AS
        N'?c',  --COLLATE Latin1_General_CI_AS
        N'?c',  --COLLATE Latin1_General_CI_AS
        N'?c'   --COLLATE Latin1_General_CI_AS

INSERT INTO testT
SELECT  N'C'    --COLLATE Latin1_General_CI_AS 
        ,N'C'   --COLLATE Latin1_General_CI_AS
        ,N'C'   --COLLATE Latin1_General_CI_AS
        ,N'C'   --COLLATE SQL_Latin1_General_CP1_CI_AS

SELECT * FROM testT;

DROP TABLE testT;
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 11

问题是,旧的SQL Server排序规则(即那些开始的名字SQL_)和Windows排序规则的前两个版本(在80其附带的SQL Server 2000,并且没有在名称的版本号系列,以及90一系列SQL Server 2005 附带)缺少许多字符的排序权重。从100SQL Server 2008 附带的系列排序规则开始,这大部分得到纠正。

正如您在下面的示例中看到的,?当使用非二进制版本 80 或 90 排序规则(和 SQL Server 排序规则)时,该字符匹配空字符串,因为它们都具有相同的排序权重:0。没有。纳达。这意味着当N'?c'N'C'(使用系列前的 100 排序规则)进行比较N'c'时,您实际上是在与N'C'(测试 #1)进行比较:

SELECT 1 WHERE N'?c' = N'C' COLLATE Latin1_General_CS_AS;
-- no result (due to "c" and "C" being different case)

SELECT 2 WHERE N'?' = N'' COLLATE SQL_Latin1_General_CP1_CI_AS;
SELECT 3 WHERE N'?' = N'' COLLATE Latin1_General_CI_AS;

SELECT 4 WHERE N'?' = N'' COLLATE Latin1_General_BIN2;
-- no result (due to "?" still being a code point and empty string has no code points)

SELECT 5 WHERE N'?' = N'' COLLATE Latin1_General_100_CI_AS;
-- no result (due to "?" finally having a sort weight in 100 series Collations)

SELECT 6 WHERE N'?' = N'' COLLATE Chinese_PRC_CI_AI;
SELECT 7 WHERE N'?' = N'' COLLATE Chinese_PRC_90_CI_AI;

SELECT 8 WHERE N'?' = N'' COLLATE Indic_General_90_CI_AI;
SELECT 9 WHERE N'?' = N'' COLLATE Indic_General_100_CI_AI;
-- no result (due to "?" finally having a sort weight in 100 series Collations)
Run Code Online (Sandbox Code Playgroud)

因此,不幸的是,您将需要删除 PK,将列更改为具有 100 级排序规则(例如Latin1_General_100_CI_AS_SC),然后重新创建 PK。请注意,建议的排序规则与当前排序规则的区别在于结尾的the100 the _SC,这使其能够正确处理补充字符。

但这并不意味着你需要:

  1. 更改其他表的排序规则(除非它们NVARCHAR在 PK 中具有相同的设置)
  2. 更改数据库的默认排序规则。不更改数据库的排序规则的主要问题是,由于变量和字符串文字使用数据库的默认排序规则,因此执行table.column = N'?'和操作之间存在行为差异@variable = N'?'

有关此行为的更多示例,请参阅我的以下博客文章的“补充字符”部分:

统一代码:搜索 T-SQL 标识符的真实有效字符列表,第 3 部分,共 2 部分(分隔标识符)