N'Șc 'considerou chave duplicada de N'C' usando o agrupamento Latin1_General_CI_AS


11

Eu tenho uma tabela com uma chave exclusiva que inclui uma NVARCHAR(50)coluna (correta ou não, mas existe). Portanto, ao tentar inserir Școu C(não importa a ordem da inserção), ele quebra na segunda inserção devido a problemas de agrupamento. Aqui está o erro:

(1 linha (s) afetada) Msg 2601, Nível 14, Estado 1, Linha 16 Não é possível inserir linha de chave duplicada no objeto 'dbo.testT' com o índice exclusivo 'IX_TestT'. O valor da chave duplicada é (C).

Selecionar devoluções:

insira a descrição da imagem aqui

O agrupamento padrão do banco de dados é Latin1_General_CI_AS. Passei algum tempo pensando em como resolvê-lo, sem alterar muito a estrutura já existente, mas não consigo encontrar uma maneira de começar a trabalhar. Tentei diferentes agrupamentos e combinações, tudo falha. Leia ( aqui e aqui ) sobre expansões de personagens e assim por diante, ainda emperrado. Aqui está um código de exemplo que estou usando para replicar o problema, sinta-se à vontade para modificar e recomendar qualquer coisa que possa ajudar a resolver isso.

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;

Respostas:


10

O problema é que os antigos agrupamentos do SQL Server (ou seja, aqueles com nomes começando SQL_) e as duas primeiras versões dos agrupamentos do Windows (a 80série que acompanha o SQL Server 2000 e não possui um número de versão no nome e a 90série que faltam os pesos de classificação para muitos caracteres. Isso foi corrigido principalmente a partir das 100séries Collations que acompanham o SQL Server 2008.

Como você pode ver nos exemplos abaixo, o Șcaractere corresponde a uma string vazia ao usar Collations não binários da versão 80 ou 90 (e SQL Server Collations), pois ambos têm o mesmo peso de classificação: 0. Nada. Nada. Isso significa que, ao comparar N'Șc'com N'C'(usando 100 agrupamentos pré-série), você está realmente comparando N'c'com N'C'(teste nº 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)

Portanto, infelizmente, você precisará soltar o PK, alterar a coluna para obter um agrupamento de 100 níveis (por exemplo Latin1_General_100_CI_AS_SC) e recriar o PK. Por favor, note que a diferença de que sugeria Agrupamento dos atuais Agrupamento é tanto o 100 e a _SC, no final, o que lhe permite lidar adequadamente com caracteres suplementares.

Isso não significa que você precisa:

  1. alterar os agrupamentos de outras tabelas (a menos que tenham a mesma configuração NVARCHARno PK)
  2. altere o agrupamento padrão do banco de dados. O principal problema com a não alteração do agrupamento do banco de dados é que haverá uma diferença de comportamento entre fazer table.column = N'Ș'e @variable = N'Ș'uma vez que variáveis ​​e literais de seqüência de caracteres usam o agrupamento padrão do banco de dados.

Para obter mais exemplos desse comportamento, consulte a seção "Caracteres complementares" da seguinte postagem no blog:

O código unificado: a busca pela lista verdadeira de caracteres válidos para identificadores T-SQL, parte 3 de 2 (identificadores delimitados)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.