Impacto en el rendimiento de Latin1_General_BIN al cambiar la clasificación predeterminada de la base de datos

16

He configurado la intercalación de la base de datos en Latin1_General_BIN, para hacer comparaciones de cadenas entre mayúsculas y minúsculas. ¿Tendrá esto un impacto en el rendimiento? ¿Tendrá algún impacto en las operaciones DML o DDL en la base de datos? La base de datos ya existe con tablas en ella.

Rakesh
fuente

Respuestas:

24

Las intercalaciones en SQL Server determinan las reglas para unir y ordenar datos de caracteres. Normalmente, primero elegiría una recopilación basada en la semántica de comparación y el orden de clasificación que requieren los consumidores de los datos.

Los humanos generalmente no encuentran que las colaciones binarias produzcan los comportamientos de clasificación y comparación que esperan. Entonces, aunque estos ofrecen el mejor rendimiento (especialmente las versiones BIN2 de punto de código puro), la mayoría de las implementaciones no los usan.

Los siguientes en términos de rendimiento sin formato (pero solo para cadenas que no son Unicode) son las intercalaciones SQL de compatibilidad con versiones anteriores . Cuando se trabaja con datos Unicode, estas intercalaciones utilizan un intercalación de Windows en su lugar, con las mismas características de rendimiento. Aquí hay trampas sutiles, por lo que debe tener buenas razones para elegir una clasificación SQL en estos días (a menos que trabaje en un sistema de EE. UU., Donde todavía es el valor predeterminado).

Las intercalaciones de Windows son las más lentas, en general, debido a las complejas reglas de comparación y clasificación de Unicode. Sin embargo, estos ofrecen compatibilidad completa con Windows en SQL Server y se mantienen regularmente para mantenerse al día con los cambios en el estándar Unicode. Para un uso moderno que incluye datos Unicode, generalmente se recomienda una intercalación de Windows.

TL; DR

Si todo lo que quiere es una semántica de clasificación y comparación que distinga entre mayúsculas y minúsculas , debe elegir la _CS_variación (para mayúsculas y minúsculas ) de la clasificación base que proporcione el comportamiento esperado para el idioma y la cultura de sus usuarios. Por ejemplo, ambos son colaciones sensibles a mayúsculas y minúsculas:

-- Latin1-General, case-sensitive, accent-sensitive
Latin1_General_CS_AS 

-- Latin1-General, case-sensitive, accent-sensitive for Unicode Data, 
-- SQL Server Sort Order 51 on Code Page 1252 for non-Unicode Data
SQL_Latin1_General_CP1_CS_AS

Puede ver estas definiciones usando sys.fn_helpcollations

Ejemplos

Cuatro tablas que son exactamente iguales excepto por la colación; uno binario, uno sensible a mayúsculas y minúsculas, uno insensible a mayúsculas y uno SQL:

CREATE TABLE #Example_BIN
(
    string nvarchar(50) 
        COLLATE Latin1_General_BIN
        NOT NULL
);

CREATE TABLE #Example_CS
(
    string nvarchar(50) 
        COLLATE Latin1_General_CS_AI
        NOT NULL
);

CREATE TABLE #Example_CI
(
    string nvarchar(50) 
        COLLATE Latin1_General_CI_AI
        NOT NULL
);

CREATE TABLE #Example_SQL
(
    string varchar(50) -- Note varchar
        COLLATE SQL_Latin1_General_CP1_CS_AS
        NOT NULL
);

Mismos datos de muestra para cada tabla:

INSERT #Example_BIN
    (string)
VALUES
    (N'A'),
    (N'a'),
    (N'B'),
    (N'b'),
    (N'C'),
    (N'c');

INSERT #Example_CS
SELECT EB.string 
FROM #Example_BIN AS EB;

INSERT #Example_CI
SELECT EB.string 
FROM #Example_BIN AS EB;

INSERT #Example_SQL
SELECT EB.string 
FROM #Example_BIN AS EB;

Ahora queremos encontrar cadenas mayores que 'a':

SELECT EB.string AS BIN
FROM #Example_BIN AS EB
WHERE EB.string > N'a'
ORDER BY EB.string;

SELECT EC.string AS CS
FROM #Example_CS AS EC
WHERE EC.string > N'a'
ORDER BY EC.string;

SELECT EC2.string AS CI
FROM #Example_CI AS EC2
WHERE EC2.string > N'a'
ORDER BY EC2.string;

SELECT ES.string AS SQL
FROM #Example_SQL AS ES
WHERE ES.string > 'a' -- not Unicode
ORDER BY ES.string;

Resultados:

╔═════╗
 BIN 
╠═════╣
 b   
 c   
╚═════╝

╔════╗
 CS 
╠════╣
 A  
 b  
 B  
 c  
 C  
╚════╝

╔════╗
 CI 
╠════╣
 B  
 b  
 C  
 c  
╚════╝

╔═════╗
 SQL 
╠═════╣
 B   
 b   
 C   
 c   
╚═════╝

Finalmente...

Sin embargo, tenga en cuenta que si usamos un literal Unicode con la clasificación SQL, las reglas de conversión implícitas resultan en una comparación de clasificación de Windows:

SELECT ES.string AS SQL
FROM #Example_SQL AS ES
WHERE ES.string > N'a'
ORDER BY ES.string;

... y los resultados de la clasificación SQL cambian :

╔═════╗
 SQL 
╠═════╣
 A   
 B   
 b   
 C   
 c   
╚═════╝
Paul White reinstala a Monica
fuente
10

Dado que esta es una base de datos existente que ya tiene tablas definidas, existen algunas implicaciones muy serias en la acción de cambiar la recopilación de la base de datos, más allá del impacto potencial en el rendimiento de las operaciones DML (que en realidad ya estaba allí). Hay un impacto muy real en el rendimiento y la funcionalidad, y este cambio no solo no logró el objetivo previsto (al menos no de manera consistente), sino que también alteró el comportamiento (o alterará el comportamiento cuando se crean nuevas tablas) en términos de cómo se ordenan y equiparan los datos.

Paul ya proporcionó una buena explicación y ejemplos de las diferencias en el rendimiento y el comportamiento entre los diferentes tipos de colaciones en su respuesta, por lo que no lo repetiré aquí. Sin embargo, algunos puntos necesitan algunos detalles adicionales, y hay varios otros puntos para agregar en relación con el escenario actual de cambiar la clasificación de una base de datos existente, en lugar de establecer la clasificación de una nueva base de datos.

  1. Las colaciones binarias son más que solo mayúsculas y minúsculas: ¡son todo sensibles! Entonces, al usar una intercalación binaria (que termina en _BINo _BIN2), sus comparaciones ahora también son sensibles al acento, sensibles a la kana, al ancho y potencialmente al gluten (al menos esa parece ser la tendencia en estos días ;-)). ¿Era este el efecto deseado de hacer este cambio? ¿Los usuarios finales esperan este cambio de comportamiento?

  2. Las intercalaciones afectan no solo las comparaciones, sino también la clasificación. A colación binaria va a clasificar en base a la ASCIIo UNICODEvalor de byte (dependiendo de VARCHARo NVARCHAR, respectivamente) de cada byte . Por lo tanto, al elegir una intercalación binaria, está renunciando a las reglas de ponderación específicas del idioma / cultura que ordenan cada carácter (incluso caracteres en algún idioma, como el húngaro, que se componen de 2 letras) de acuerdo con el alfabeto de esa cultura. Entonces, si "ch" debería aparecer naturalmente después de "k", bueno, eso no sucederá usando una intercalación binaria. Nuevamente, ¿fue este el efecto deseado de hacer este cambio? ¿Los usuarios finales esperan este cambio de comportamiento?

  3. A menos que tenga requisitos específicos de compatibilidad con versiones anteriores para su aplicación, debe usar las intercalaciones en BIN2lugar de las BINintercalaciones, suponiendo, por supuesto, que desea una colación binaria en primer lugar. Las BIN2intercalaciones se introdujeron en SQL Server 2005 y, de acuerdo con la página de MSDN para Pautas para el uso de intercalaciones BIN y BIN2 :

    Las intercalaciones binarias anteriores en SQL Server, aquellas que terminan con "_BIN", realizaron una comparación incompleta de código a punto de código para datos Unicode. Las clasificaciones binarias más antiguas de SQL Server compararon el primer carácter como WCHAR, seguido de una comparación byte por byte.

    ...

    Puede migrar a las intercalaciones binarias [_BIN2] para aprovechar las verdaderas comparaciones de puntos de código, y debe utilizar las nuevas intercalaciones binarias para el desarrollo de nuevas aplicaciones.

    También se debe tener en cuenta que las _BIN2intercalaciones coinciden convenientemente con el comportamiento de la Ordinalopción de la Enumeración de comparación de cadenas , de modo que las comparaciones y la ordenación realizadas en código .NET usando esa opción producirán los mismos resultados que las mismas operaciones que se realizan dentro de SQL Server (cuando se usa las _BIN2colaciones, por supuesto).

  4. Por razones similares a lo que se acaba de decir sobre las _BIN2intercalaciones, a menos que tenga requisitos específicos para mantener un comportamiento de compatibilidad con versiones anteriores, debe inclinarse hacia el uso de intercalaciones de Windows y no de intercalaciones específicas de SQL Server (es decir, las que comienzan SQL_ahora se consideran un poco "sucky" ;-)).

  5. Al utilizar datos Unicode (es decir, el prefijo de cadena No entrar en SQL Server del código de la aplicación, donde el tipo de datos se ha especificado como NCharo NVarChar), no veo cómo el uso de una intercalación vs otra haría una diferencia para insertar o actualizar una NCHARo NVARCHARcampo de cadena .

    Al usar datos que no son Unicode, o al insertar o actualizar un campo que no es Unicode, entonces la clasificación particular (base de datos o campo) podría desempeñar un pequeño papel si los caracteres que se insertan / actualizan necesitan ser traducidos, o no son asignables (es que incluso una palabra?), como se especifica en la página de códigos que se define por la clasificación. Por supuesto, este problema potencial existe cada vez que se usan datos o tipos de datos que no son Unicode, y no es específico de este escenario de cambiar la intercalación de la base de datos. Ese cambio afectará a los literales de cadena (lo que podría haber sido un problema si la intercalación de la base de datos fuera diferente a la intercalación del campo). Pero incluso si no se realiza ningún cambio en la intercalación de la base de datos, los datos que ingresan desde otras bases de datos o desde fuera de SQL Server (cualquier código de cliente) pueden contener caracteres y tener una codificación particular.

  6. ¡¡¡MUY IMPORTANTE!!! Al cambiar la clasificación predeterminada de la base de datos, la clasificación especificada para cualquier campo de cadena existente en cualquier tabla existente no cambiará, pero cualquier campo nuevo tendrá una clasificación de la base de datos predeterminada (a menos que se anule mediante la COLLATEcláusula). Esto afectará sus consultas de tres maneras:

    1) Si alguna consulta se UNE en cualquiera de esos campos existentes a cualquiera de los nuevos campos, obtendrá un error de desajuste de intercalación:

    USE [master];
    GO
    
    IF (DB_ID(N'ChangeCollationTest') IS NOT NULL)
    BEGIN
        PRINT 'Dropping [ChangeCollationTest] DB...';
        ALTER DATABASE [ChangeCollationTest]
            SET SINGLE_USER
            WITH ROLLBACK IMMEDIATE;
    
        DROP DATABASE [ChangeCollationTest];
    END;
    GO
    
    PRINT 'Creating [ChangeCollationTest] DB...';
    CREATE DATABASE [ChangeCollationTest]
        COLLATE SQL_Latin1_General_CP1_CI_AS;
    GO
    
    USE [ChangeCollationTest];
    GO
    
    CREATE TABLE [CollateTest-SQL_Latin1_General_CP1_CI_AS]
                 (Col1 NVARCHAR(50) COLLATE DATABASE_DEFAULT, Col2 NVARCHAR(50));
    SELECT *
    FROM   sys.columns sc
    WHERE  sc.[object_id] = OBJECT_ID(N'[CollateTest-SQL_Latin1_General_CP1_CI_AS]');
    -- "collation_name" for both fields shows: SQL_Latin1_General_CP1_CI_AS
    GO
    
    USE [master];
    GO
    ALTER DATABASE [ChangeCollationTest]
        COLLATE Latin1_General_BIN2;
    GO
    USE [ChangeCollationTest];
    GO
    
    CREATE TABLE [CollateTest-Latin1_General_BIN2]
                 (Col1 NVARCHAR(50) COLLATE DATABASE_DEFAULT, Col2 NVARCHAR(50));
    SELECT *
    FROM   sys.columns sc
    WHERE  sc.[object_id] = OBJECT_ID(N'[CollateTest-Latin1_General_BIN2]');
    -- "collation_name" for both fields shows: Latin1_General_BIN2
    GO
    
    
    SELECT *
    FROM   dbo.[CollateTest-SQL_Latin1_General_CP1_CI_AS] ctSQL
    INNER JOIN  dbo.[CollateTest-Latin1_General_BIN2] ctWIN
            ON  ctWIN.Col1 = ctSQL.Col1;

    Devoluciones:

    Msg 468, Level 16, State 9, Line 4
    Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and
    "Latin1_General_BIN2" in the equal to operation.

    2) Los predicados / filtros en los campos existentes de las tablas existentes (configurados en la clasificación predeterminada anterior) que se comparan con los literales de cadena o las variables no producirán errores, pero ciertamente podrían verse afectados en términos de rendimiento debido a que SQL Server necesita igualar la clasificación de ambos lados y convirtiendo automáticamente la cadena literal o variable a la clasificación del campo. Habilite "Incluir plan de ejecución real" (Control-M) y luego ejecute lo siguiente (suponiendo que ya haya ejecutado las consultas que se muestran arriba):

    SELECT *
    FROM   dbo.[CollateTest-SQL_Latin1_General_CP1_CI_AS] ctSQL
    WHERE  ctSQL.Col1 = N'a';
    -- Unspecified collations on string literals and variables assume the database default
    -- collation. This mismatch doesn't cause an error because SQL Server adds a
    -- "[Col1]=CONVERT_IMPLICIT(nvarchar(4000),[@1],0)" but it can hurt performance.
    
    SELECT *
    FROM   dbo.[CollateTest-Latin1_General_BIN2] ctWIN
    WHERE  ctWIN.Col1 = N'a';
    -- No CONVERT_IMPLICIT; plan shows "[Col1]=[@1]".

    3) Y, hablando de conversiones implícitas, observe cómo es el literal de cadena (con una clasificación implícita de la clasificación predeterminada de la base de datos:) Latin1_General_BIN2que se convierte, no el campo en la tabla. ¿Alguna suposición sobre si este filtro será o no sensible a mayúsculas y minúsculas (la antigua colación) o sensible a mayúsculas y minúsculas (la nueva colación)? Ejecute lo siguiente para ver:

    INSERT INTO dbo.[CollateTest-SQL_Latin1_General_CP1_CI_AS] (Col1)
    VALUES (N'a'), (N'A');
    
    SELECT ctSQL.Col1
    FROM   dbo.[CollateTest-SQL_Latin1_General_CP1_CI_AS] ctSQL
    WHERE  ctSQL.Col1 = N'a';

    Devoluciones:

    Col1
    ----
    a
    A

    D'oh! No solo hay un ligero (¿o quizás más significativo?) Impacto en el rendimiento de esta consulta debido a la CONVERT_IMPLICIT(), sino que ni siquiera se comporta de la manera que distingue entre mayúsculas y minúsculas.

    Ergo, si la clasificación se cambia en un DB que ya tiene tablas, entonces sí, tanto el rendimiento como la funcionalidad se ven afectados.

    Si la intercalación se establece en una nueva base de datos, entonces Paul ya cubrió eso al explicar cómo una intercalación binaria, aunque rápida, probablemente no se clasificará de la manera que uno esperaría o desearía.


También debe tenerse en cuenta que siempre puede especificar intercalaciones por condición. La cláusula COLLATE se puede agregar a las WHEREcondiciones ORDER BYy a casi cualquier lugar que acepte una cadena.

Ejemplo 1 (DONDE condición):

SELECT tmp.col AS [SQL-CaseSensitive]
FROM (VALUES ('a'), ('A'), ('b'), ('B')) tmp(col)
WHERE tmp.col > 'a' COLLATE SQL_Latin1_General_CP1_CS_AS;

SELECT tmp.col AS [Windows-CaseSensitive]
FROM (VALUES ('a'), ('A'), ('b'), ('B')) tmp(col)
WHERE tmp.col > 'a' COLLATE Latin1_General_CS_AI;

Devoluciones:

SQL-CaseSensitive
-----------------
b
B

Windows-CaseSensitive
-----------------
A
b
B

Ejemplo 2 (ORDENAR POR):

SELECT tmp.col AS [Windows-CaseSensitive]
FROM (VALUES ('a'), ('A'), ('b'), ('B')) tmp(col)
ORDER BY tmp.col COLLATE Latin1_General_CS_AI;

SELECT tmp.col AS [Windows-Binary]
FROM (VALUES ('a'), ('A'), ('b'), ('B')) tmp(col)
ORDER BY tmp.col COLLATE Latin1_General_BIN2;

Devoluciones:

Windows-CaseSensitive
-----------------
a
A
b
B

Windows-Binary
-----------------
A
B
a
b

Ejemplo 3 (declaración IF):

IF ('A' = 'a') SELECT 1 AS [DatabaseDefault-CaseInsensitive?];
-- if the DB is not case-sensitive or binary, returns 1

IF ('A' = 'a' COLLATE Latin1_General_BIN2) SELECT 2 AS [Windows-Binary];

Devoluciones:

DatabaseDefault-CaseInsensitive?
--------------------------------
1

{nothing}

Ejemplo 4 (asociado con el parámetro de entrada de función):

SELECT  UNICODE(N'🂡') AS [UCS-2],
        UNICODE(N'🂡' COLLATE Latin1_General_100_CI_AS_SC) AS [UTF-16];
-- This character is a Unicode supplemental character and is not part of the
-- default UCS-2 encoding. In order for built-in functions to handle these
-- characters correctly, either the DB default collation needs to end in
-- "_SC" (available as of SQL Server 2012), or use as shown here.
-- See the character in more detail here: http://unicode-table.com/en/1F0A1/

Devoluciones:

UCS-2    UTF-16
------   -------
55356    127137

El valor UCS-2 de 55,356 es parcialmente correcto porque es el primero de los dos valores en el "par sustituto". Pero a menos que se indique explícitamente la _SCintercalación, la UNICODE()función solo puede ver cada carácter como un valor de doble byte y no sabe cómo manejar adecuadamente un par sustituto de doble byte doble.


ACTUALIZAR

Incluso con todos los ejemplos anteriores, un aspecto de las comparaciones entre mayúsculas y minúsculas que generalmente se pasa por alto y se niega mediante comparaciones / colaciones binarias, es la normalización (composición y descomposición) que forma parte de Unicode.

Ejemplo 5 (cuando una comparación binaria no distingue entre mayúsculas y minúsculas):

Las verdaderas comparaciones entre mayúsculas y minúsculas permiten combinar caracteres que, en combinación con otro carácter, forman otro carácter que ya existe como otro punto de código Unicode. Las comparaciones entre mayúsculas y minúsculas se preocupan por el carácter visualizable, no por los puntos de código utilizados para crearlo.

SELECT 'Equal' AS [Binary],
       NCHAR(0x00FC) AS [ü],
       N'u' + NCHAR(0x0308) AS [u + combining diaeresis]
WHERE  NCHAR(0x00FC) COLLATE Latin1_General_100_BIN2
    =  N'u' + NCHAR(0x0308) COLLATE Latin1_General_100_BIN2
-- No result as they are a different number of code points,
-- as well as being different code points.

SELECT 'Equal' AS [Case-Sensitive],
       NCHAR(0x00FC) AS [ü],
       N'u' + NCHAR(0x0308) AS [u + combining diaeresis]
WHERE  NCHAR(0x00FC) COLLATE Latin1_General_100_CS_AS -- ü
    =  N'u' + NCHAR(0x0308) COLLATE Latin1_General_100_CS_AS -- u + combining diaeresis
-- Result set returned, even being a different number of code points AND Accent Sensitive,
-- due to normalization

Devoluciones:

Binary            ü     u + combining diaeresis
-------          ---   -------------------------
{nothing}

Case-Sensitive    ü     u + combining diaeresis
---------------  ---   -------------------------
Equal             ü     ü

Las verdaderas comparaciones entre mayúsculas y minúsculas también permiten que los caracteres anchos equivalgan a sus equivalentes no anchos.

IF (N'sofia' = N'sofia' COLLATE Latin1_General_100_BIN2)
  SELECT 'Values are the same' AS [Binary]
ELSE
  SELECT 'Values are different' AS [Binary];


IF (N'sofia' = N'sofia' COLLATE Latin1_General_100_CS_AS)
  SELECT 'Values are the same' AS [Case-Sensitive]
ELSE
  SELECT 'Values are different' AS [Case-Sensitive];

Devoluciones:

Binary
---------------
Values are different


Case-Sensitive
---------------
Values are the same

Es decir:

¡Las colaciones BINARIAS ( _BINy _BIN2) no distinguen entre mayúsculas y minúsculas!

Solomon Rutzky
fuente