Die Leistung von Latin1_General_BIN wird beeinträchtigt, wenn die Standardkollatierung der Datenbank geändert wird

16

Ich habe die Datenbanksortierung auf festgelegt Latin1_General_BIN, um Zeichenfolgenvergleiche zwischen Groß- und Kleinschreibung zu berücksichtigen. Wird sich dies auf die Leistung auswirken? Hat dies Auswirkungen auf DML- oder DDL-Vorgänge in der Datenbank? Die Datenbank ist bereits mit Tabellen vorhanden.

Rakesh
quelle

Antworten:

24

Kollatierungen in SQL Server legen die Regeln für das Abgleichen und Sortieren von Zeichendaten fest. Normalerweise wählen Sie zuerst eine Kollatierung basierend auf der Vergleichssemantik und der Sortierreihenfolge, die die Verbraucher der Daten benötigen.

Menschen stellen im Allgemeinen nicht fest, dass binäre Kollatierungen das erwartete Sortier- und Vergleichsverhalten erzeugen. Obwohl diese die beste Leistung bieten (insbesondere die reinen Code-Point-BIN2-Versionen), werden sie von den meisten Implementierungen nicht verwendet.

Als Nächstes folgen in Bezug auf die Leistung (jedoch nur für Nicht-Unicode-Zeichenfolgen) die abwärtskompatiblen SQL-Kollatierungen . Bei der Arbeit mit Unicode-Daten wird für diese Sortierungen stattdessen eine Windows-Sortierung mit denselben Leistungsmerkmalen verwendet. Hier gibt es subtile Fallen, sodass Sie heutzutage gute Gründe haben müssen, eine SQL-Kollatierung zu wählen (es sei denn, Sie arbeiten auf einem US-amerikanischen System, auf dem dies immer noch der Standard ist).

Windows-Kollatierungen sind im Allgemeinen aufgrund der komplexen Unicode-Vergleichs- und Sortierregeln am langsamsten. Diese bieten jedoch vollständige Kompatibilität mit Windows in SQL Server und werden regelmäßig gewartet, um mit den Änderungen des Unicode-Standards Schritt zu halten. Für die moderne Verwendung mit Unicode-Daten wird im Allgemeinen eine Windows-Sortierung empfohlen.

TL; DR

Wenn Sie nur zwischen Groß- und Kleinschreibung unterscheiden und die Semantik sortieren möchten , sollten Sie die _CS_(für Groß- und Kleinschreibung ) Variante der Basissortierung auswählen, die das erwartete Verhalten für die Sprache und Kultur Ihrer Benutzer bietet. Bei beiden handelt es sich beispielsweise um Sortierungen, bei denen die Groß- und Kleinschreibung berücksichtigt wird:

-- 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

Sie können diese Definitionen mit sys.fn_helpcollations anzeigen

Beispiele

Vier Tabellen , die bis auf die Sortierung genau gleich sind. Eine binäre, eine case-sensitive, eine case-insensitive und eine SQL case-sensitive:

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
);

Gleiche Beispieldaten für jede Tabelle:

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;

Jetzt wollen wir Zeichenfolgen finden, die größer als 'a' sind:

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;

Ergebnisse:

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

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

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

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

Schließlich...

Beachten Sie jedoch, dass die impliziten Konvertierungsregeln zu einem Vergleich der Windows-Sortierung führen, wenn für die SQL-Sortierung ein Unicode-Literal verwendet wird:

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

... und die SQL-Kollatierungsergebnisse ändern sich :

╔═════╗
 SQL 
╠═════╣
 A   
 B   
 b   
 C   
 c   
╚═════╝
Paul White sagt GoFundMonica
quelle
10

Angesichts der Tatsache, dass es sich um eine vorhandene Datenbank handelt , in der bereits Tabellen definiert sind, ergeben sich einige schwerwiegende Auswirkungen auf die Aktion zum Ändern der Datenbanksortierung, die über die potenziellen Auswirkungen auf die Leistung auf DML-Operationen (die tatsächlich bereits vorhanden waren) hinausgehen. Leistung und Funktionalität haben sehr reale Auswirkungen, und diese Änderung hat nicht nur das angestrebte Ziel (zumindest nicht konsistent) erreicht, sondern auch das Verhalten (oder das Verhalten, wenn neue Tabellen erstellt werden) in Bezug auf wahrscheinlich geändert wie Daten geordnet und gleichgesetzt werden.

Paul hat in seiner Antwort bereits gute Erklärungen und Beispiele für die Unterschiede in Leistung und Verhalten zwischen den verschiedenen Arten von Kollatierungen geliefert, daher werde ich das hier nicht wiederholen. Für einige Punkte sind jedoch einige zusätzliche Details erforderlich, und in Bezug auf das aktuelle Szenario zum Ändern der Kollatierung einer vorhandenen Datenbank müssen einige weitere Punkte hinzugefügt werden, anstatt die Kollatierung einer neuen Datenbank festzulegen.

  1. Binäre Kollatierungen sind mehr als nur zwischen Groß- und Kleinschreibung zu unterscheiden. Sie sind allesamt vertraulich! Wenn Sie also eine binäre Sortierung verwenden (die mit _BINoder endet _BIN2), sind Ihre Vergleiche jetzt auch akzent-, kana-, breiten- und potenziell glutenempfindlich (zumindest scheint dies heutzutage der Trend zu sein ;-)). War dies der gewünschte Effekt dieser Änderung? Erwarten Endbenutzer diese Verhaltensänderung?

  2. Kollatierungen wirken sich nicht nur auf Vergleiche, sondern auch auf die Sortierung aus. Eine binäre Kollatierung wird basierend auf dem ASCIIoder dem UNICODEBytewert (abhängig von VARCHARbzw. NVARCHAR) jedes Bytes sortiert . Wenn Sie also eine binäre Sortierung auswählen, geben Sie sprach- / kulturspezifische Gewichtungsregeln auf, die jedes Zeichen (auch Zeichen in einer Sprache wie Ungarisch, die aus zwei Buchstaben bestehen) gemäß dem Alphabet dieser Kultur sortieren. Also, wenn "ch" natürlich nach "k" kommen sollte, passiert das nicht mit einer binären Kollation. War dies wiederum der gewünschte Effekt bei dieser Änderung? Erwarten Endbenutzer diese Verhaltensänderung?

  3. Sofern Sie für Ihre Anwendung keine speziellen Abwärtskompatibilitätsanforderungen haben, sollten Sie die BIN2anstelle von BINKollatierungen verwenden, vorausgesetzt natürlich, Sie möchten zunächst eine binäre Kollatierung. Die BIN2Kollatierungen wurden in SQL Server 2005 und gemäß der MSDN-Seite für Richtlinien für die Verwendung von BIN- und BIN2-Kollatierungen eingeführt :

    Frühere binäre Kollatierungen in SQL Server, die mit "_BIN" enden, führten einen unvollständigen Code-Punkt-zu-Code-Punkt-Vergleich für Unicode-Daten durch. Ältere SQL Server-Binärsortierungen verglichen das erste Zeichen als WCHAR, gefolgt von einem byteweisen Vergleich.

    ...

    Sie können zu den binären Sortierungen [_BIN2] migrieren, um echte Codepunktvergleiche zu nutzen, und Sie sollten die neuen binären Sortierungen für die Entwicklung neuer Anwendungen verwenden.

    Es ist auch zu beachten, dass die Kollatierungen_BIN2 dem Verhalten der OrdinalOption StringComparison Enumeration in geeigneter Weise entsprechen , sodass Vergleiche und Sortierungen im .NET-Code mit dieser Option zu denselben Ergebnissen führen wie die gleichen Vorgänge, die in SQL Server ausgeführt werden (wenn verwendet) die _BIN2Kollatierungen natürlich).

  4. Aus ähnlichen Gründen wie oben in Bezug auf die _BIN2Kollatierungen angegeben, sollten Sie, sofern Sie keine besonderen Anforderungen zur Aufrechterhaltung des Abwärtskompatibilitätsverhaltens haben, eher die Windows-Kollatierungen als die SQL Server-spezifischen Kollatierungen verwenden (dh die Kollatierungen, die mit beginnen, SQL_werden jetzt berücksichtigt ein bisschen "sucky" ;-)).

  5. Wenn Unicode - Daten unter Verwendung von (dh Zeichenfolge mit dem Präfix Noder von App - Code in SQL Server kommen , wo der Datentyp wie angegeben wurde NCharoder NVarChar), sehe ich nicht , wie gegen eine andere Sortierung verwendet wird, einen Unterschied zum Einfügen oder Aktualisieren eines machen würde NCHARoder NVARCHARString - Feldes .

    Wenn Sie Nicht-Unicode-Daten verwenden oder ein Nicht-Unicode-Feld einfügen oder aktualisieren, spielt die jeweilige Kollatierung (Datenbank oder Feld) möglicherweise eine geringe Rolle, wenn Zeichen, die eingefügt / aktualisiert werden, übersetzt werden müssen oder nicht zugeordnet werden können (ist) das sogar ein Wort?), wie in der Codepage angegeben, die durch die Kollatierung definiert ist. Dieses potenzielle Problem tritt natürlich immer dann auf, wenn Nicht-Unicode-Daten oder -Datentypen verwendet werden, und ist nicht spezifisch für dieses Szenario zum Ändern der DB-Kollatierung. Diese Änderung wirkt sich auf Zeichenfolgenliterale aus (was möglicherweise bereits ein Problem war, wenn sich die DB-Kollatierung von der Kollatierung des Felds unterschied). Aber selbst wenn keine Änderungen an der DB-Kollatierung vorgenommen werden, können Daten, die von anderen DBs oder von außerhalb von SQL Server (einem beliebigen Client-Code) eingehen, beliebige Zeichen enthalten und eine bestimmte Codierung aufweisen.

  6. SEHR WICHTIG!!! Wenn Sie die Standardkollatierung der Datenbank ändern, wird die für vorhandene Zeichenfolgenfelder in vorhandenen Tabellen angegebene Kollatierung nicht geändert. Neue Felder enthalten jedoch eine Kollatierung der Standarddatenbank (sofern diese nicht über die COLLATEKlausel überschrieben wird ). Dies hat drei Auswirkungen auf Ihre Abfragen:

    1) Wenn bei einer Abfrage von JOIN für eines dieser vorhandenen Felder ein neues Feld ausgewählt wird, wird ein Kollatierungsfehler angezeigt:

    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;

    Kehrt zurück:

    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) Prädikate / Filter für vorhandene Felder vorhandener Tabellen (auf die vorherige Standardkollatierung festgelegt), die mit Zeichenfolgenliteralen oder -variablen verglichen werden, sind fehlerfrei, können jedoch in Bezug auf die Leistung beeinträchtigt werden, da SQL Server die Kollatierung von gleichsetzen muss beidseitig und automatische Konvertierung des String-Literal oder der Variable in die Sortierung des Feldes. Aktivieren Sie "Aktuellen Ausführungsplan einbeziehen" (Control-M) und führen Sie dann Folgendes aus (vorausgesetzt, Sie haben die oben gezeigten Abfragen bereits ausgeführt):

    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) UND: Wenn Sie von impliziten Konvertierungen sprechen, beachten Sie, dass das Zeichenfolgenliteral (mit einer impliziten Kollatierung der Datenbank-Standardkollatierung:) Latin1_General_BIN2konvertiert wird, nicht das Feld in der Tabelle. Gibt es Vermutungen, ob bei diesem Filter zwischen Groß- und Kleinschreibung unterschieden wird (alte Sortierung) oder zwischen Groß- und Kleinschreibung unterschieden wird (neue Sortierung)? Führen Sie Folgendes aus, um Folgendes anzuzeigen:

    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';

    Kehrt zurück:

    Col1
    ----
    a
    A

    D'oh! Aufgrund der CONVERT_IMPLICIT()gibt es nicht nur einen geringfügigen (oder möglicherweise bedeutenderen?) Leistungseinbruch für diese Abfrage , sondern sie verhält sich auch nicht in der gewünschten Groß- / Kleinschreibung.

    Wenn also die Sortierung in einer Datenbank geändert wird, in der bereits Tabellen vorhanden sind, sind Leistung UND Funktionalität betroffen.

    Wenn die Kollatierung in einer neuen Datenbank festgelegt wird, hat Paul dies bereits erläutert, indem er erklärte, dass eine binäre Kollatierung, obwohl sie schnell ist, wahrscheinlich nicht so sortieren kann, wie man es erwarten oder wünschen würde.


Es sollte auch beachtet werden, dass Sie immer Kollatierungen pro Bedingung angeben können. Die COLLATE- Klausel kann zu WHEREBedingungen hinzugefügt werden, ORDER BYund zwar an fast jeder Stelle, an der eine Zeichenfolge akzeptiert wird.

Beispiel 1 (WHERE-Bedingung):

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;

Kehrt zurück:

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

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

Beispiel 2 (BESTELLEN NACH):

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;

Kehrt zurück:

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

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

Beispiel 3 (IF-Anweisung):

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];

Kehrt zurück:

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

{nothing}

Beispiel 4 (assoziiert mit Funktionseingabeparameter):

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/

Kehrt zurück:

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

Der UCS-2-Wert von 55.356 ist insofern teilweise korrekt, als er der erste der beiden Werte im "Ersatzpaar" ist. Wenn die _SCSortierung nicht explizit angegeben wird , kann die UNICODE()Funktion jedes Zeichen nur als Doppelbyte-Wert anzeigen und weiß nicht, wie ein Doppelbyte-Ersatzpaar richtig behandelt wird.


AKTUALISIEREN

Selbst bei allen obigen Beispielen ist ein Aspekt der case-sensitiven Vergleiche, der normalerweise übersehen und bei binären Vergleichen / Kollatierungen negiert wird, die Normalisierung (Komposition und Zerlegung), die Teil von Unicode ist.

Beispiel 5 (wenn ein binärer Vergleich nicht zwischen Groß- und Kleinschreibung unterscheidet):

Bei echten Vergleichen, bei denen die Groß- und Kleinschreibung beachtet wird, können Zeichen kombiniert werden, die in Kombination mit einem anderen Zeichen ein weiteres Zeichen bilden, das bereits als ein anderer Unicode-Codepunkt vorhanden ist. Bei Vergleichen, bei denen die Groß- und Kleinschreibung beachtet wird, wird das anzeigbare Zeichen berücksichtigt, nicht die zum Erstellen verwendeten Codepunkte.

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

Kehrt zurück:

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

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

Echte Vergleiche, bei denen zwischen Groß- und Kleinschreibung unterschieden wird, ermöglichen es auch, dass breite Zeichen ihren nicht breiten Entsprechungen entsprechen.

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];

Kehrt zurück:

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


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

Ergo:

BINARY-Kollatierungen ( _BINund _BIN2) berücksichtigen nicht die Groß- und Kleinschreibung!

Solomon Rutzky
quelle