N'Șc 'se considera clave duplicada de N'C' usando la intercalación Latin1_General_CI_AS


11

Tengo una tabla con una clave única que incluye una NVARCHAR(50)columna (correcta o no, pero está ahí). Entonces, cuando intenta insertar Șco C(no importa el orden de la inserción) se rompe en la segunda inserción debido a problemas de intercalación. Aquí está el error:

(1 fila (s) afectada) Msg 2601, Nivel 14, Estado 1, Línea 16 No se puede insertar una fila de clave duplicada en el objeto 'dbo.testT' con el índice único 'IX_TestT'. El valor clave duplicado es (C).

Seleccionar devoluciones:

ingrese la descripción de la imagen aquí

La clasificación predeterminada de la base de datos es Latin1_General_CI_AS. Pasó algún tiempo buscando cómo resolverlo, sin cambiar demasiado la estructura ya existente, pero no puede encontrar la manera de trabajar. Intenté diferentes colaciones y combinaciones, todo falla. Lea ( aquí y aquí ) sobre expansiones de personajes, etc., aún atascado. Aquí hay un código de muestra que estoy usando para replicar el problema, siéntase libre de modificar y recomendar cualquier cosa que pueda ayudar a resolver esto.

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;

Respuestas:


10

El problema es que las antiguas intercalaciones de SQL Server (es decir, aquellas con nombres que comienzan con SQL_) y las dos primeras versiones de las intercalaciones de Windows (la 80serie que vino con SQL Server 2000 y no tienen un número de versión en el nombre, y la 90serie que vino con SQL Server 2005) faltan los pesos de clasificación para una gran cantidad de caracteres. Esto se corrigió principalmente a partir de la 100serie Intercalaciones que vino con SQL Server 2008.

Como puede ver en los ejemplos a continuación, el Șcarácter coincide con una cadena vacía cuando se utilizan intercalaciones no binarias, versión 80 o 90 (y intercalaciones de SQL Server) ya que ambas tienen el mismo peso de clasificación: 0. Nada. Nada Esto significa que cuando se compara N'Șc'con N'C'(utilizando las intercalaciones 100 de la serie previa), realmente se está comparando N'c'con N'C'(prueba # 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)

Por lo tanto, desafortunadamente tendrá que soltar la PK, alterar la columna para tener una Clasificación de 100 niveles (por ejemplo Latin1_General_100_CI_AS_SC) y luego volver a crear la PK. Tenga en cuenta que la diferencia en esa Clasificación sugerida de la Clasificación actual es tanto la 100 como la _SCal final, lo que le permite manejar correctamente los caracteres suplementarios.

Esto no significa que necesite:

  1. cambiar las intercalaciones de otras tablas (a menos que tengan la misma configuración de NVARCHARen el PK)
  2. cambiar la clasificación predeterminada de la base de datos. El problema principal con no cambiar la Clasificación de la base de datos es que habrá una diferencia de comportamiento entre hacer table.column = N'Ș'y @variable = N'Ș'dado que las variables y los literales de cadena usan la Clasificación predeterminada de la Base de datos.

Para obtener más ejemplos de este comportamiento, consulte la sección "Personajes suplementarios" de la siguiente publicación de blog mía:

El código Uni: la búsqueda de la verdadera lista de caracteres válidos para identificadores T-SQL, parte 3 de 2 (identificadores delimitados)

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.