Impatto sulle prestazioni di Latin1_General_BIN quando si modificano le regole di confronto predefinite del database


16

Ho impostato le regole di confronto del database su Latin1_General_BIN, per fare distinzione tra maiuscole e minuscole tra confronti di stringhe. Ciò avrà un impatto sulle prestazioni? Avrà un impatto sulle operazioni DML o DDL nel database? Il database esiste già con tabelle in esso.

Risposte:


24

Le regole di confronto in SQL Server determinano le regole per la corrispondenza e l'ordinamento dei dati dei caratteri. Normalmente, si sceglierebbe prima un confronto in base alla semantica di confronto e all'ordinamento richiesti dai consumatori dei dati.

Gli umani generalmente non trovano che le collazioni binarie producano i comportamenti di ordinamento e confronto che si aspettano. Pertanto, sebbene offrano le migliori prestazioni (in particolare le versioni BIN2 in puro punto di codice), la maggior parte delle implementazioni non le utilizza.

Successivamente in termini di prestazioni non elaborate (ma solo per stringhe non Unicode) sono le regole di confronto SQL con compatibilità con le versioni precedenti . Quando si lavora con i dati Unicode, queste regole di confronto utilizzano invece regole di confronto di Windows , con le stesse caratteristiche di prestazione. Ci sono trappole sottili qui, quindi devi avere buoni motivi per scegliere un confronto SQL in questi giorni (a meno che non lavori su un sistema americano, dove è ancora l'impostazione predefinita).

Le regole di confronto di Windows sono le più lente, in generale, a causa delle complesse regole di confronto e ordinamento Unicode. Tuttavia, questi offrono la completa compatibilità con Windows in SQL Server e vengono regolarmente mantenuti per tenere il passo con le modifiche allo standard Unicode. Per un uso moderno che include dati Unicode, si consiglia generalmente un confronto di Windows.

TL; DR

Se tutto ciò che desideri è la semantica di confronto e ordinamento con distinzione tra maiuscole e minuscole , dovresti scegliere la _CS_variazione (per maiuscole / minuscole ) di qualsiasi confronto di base che fornisce il comportamento previsto per la lingua e la cultura dei tuoi utenti. Ad esempio, entrambi sono fascicoli sensibili al maiuscolo / minuscolo:

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

Puoi vedere queste definizioni usando sys.fn_helpcollations

Esempi

Quattro tabelle esattamente uguali, tranne per le regole di confronto; un binario, uno con distinzione tra maiuscole e minuscole, uno senza distinzione tra maiuscole e minuscole e uno con distinzione tra maiuscole e minuscole 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
);

Stessi dati di esempio per ogni tabella:

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;

Ora vogliamo trovare stringhe maggiori di '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;

risultati:

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

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

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

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

Infine...

Nota però, se utilizziamo un valore letterale Unicode con regole di confronto SQL, le regole di conversione implicite danno luogo a un confronto delle regole di confronto di Windows:

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

... e i risultati delle regole di confronto SQL cambiano :

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

10

Dato che si tratta di un database esistente in cui sono già definite tabelle, vi sono alcune implicazioni molto gravi per l'azione di modifica delle regole di confronto del database, oltre al potenziale impatto sulle prestazioni delle operazioni DML (che in realtà era già presente). Vi è un impatto molto reale su prestazioni e funzionalità, e questo cambiamento non solo non ha raggiunto l'obiettivo previsto (almeno non in modo coerente), ma ha altrettanto modificato il comportamento (o altererà il comportamento quando vengono create nuove tabelle) in termini di come vengono ordinati ed equiparati i dati.

Paul ha già fornito una buona spiegazione ed esempi delle differenze nelle prestazioni e nel comportamento tra i diversi tipi di regole di confronto nella sua risposta, quindi non lo ripeterò qui. Tuttavia, alcuni punti richiedono alcuni dettagli aggiuntivi e ci sono molti altri punti da aggiungere in relazione allo scenario attuale di modifica delle regole di confronto di un DB esistente, anziché impostare le regole di confronto di un nuovo DB.

  1. Le regole di confronto binarie non sono solo sensibili al maiuscolo / minuscolo: sono tutto sensibile! Quindi, usando una fascicolazione binaria (che termina con _BINo _BIN2), i tuoi confronti ora sono anche sensibili all'accento, sensibili alla kana, sensibili alla larghezza e potenzialmente al glutine (almeno questa tendenza sembra essere al giorno d'oggi ;-)). È stato questo l'effetto desiderato per apportare questa modifica? Gli utenti finali si aspettano questo cambiamento di comportamento?

  2. Le regole di confronto influiscono non solo sui confronti, ma anche sull'ordinamento. Le regole di confronto binarie verranno ordinate in base al valore del byte ASCIIo UNICODE(in base a , VARCHARo NVARCHAR, rispettivamente) di ciascun byte . Quindi, scegliendo un confronto binario, stai rinunciando a regole di ponderazione specifiche per lingua / cultura che ordinano ciascun personaggio (anche i caratteri in qualche lingua, come l'ungherese, che sono composti da 2 lettere) in base all'alfabeto di quella cultura. Quindi, se "ch" dovesse naturalmente venire dopo "k", beh, ciò non accadrà usando un confronto binario. Ancora una volta, questo è stato l'effetto desiderato di apportare questo cambiamento? Gli utenti finali si aspettano questo cambiamento di comportamento?

  3. A meno che non si disponga di specifici requisiti di compatibilità con le versioni precedenti per l'applicazione, è consigliabile utilizzare le regole BIN2di BINconfronto anziché quelle , presupponendo, ovviamente, che si desideri innanzitutto una raccolta binaria. Le BIN2regole di confronto sono state introdotte in SQL Server 2005 e in base alla pagina MSDN per le Linee guida per l'utilizzo delle regole di confronto BIN e BIN2 :

    Le regole di confronto binarie precedenti in SQL Server, quelle che terminavano con "_BIN", eseguivano un confronto incompleto da punto a codice da punto a codice per i dati Unicode. Le regole di confronto binarie precedenti di SQL Server hanno confrontato il primo carattere come WCHAR, seguito da un confronto byte per byte.

    ...

    È possibile migrare alle regole di confronto binarie [_BIN2] per sfruttare i veri confronti di punti di codice e utilizzare le nuove regole di confronto binarie per lo sviluppo di nuove applicazioni.

    Va inoltre notato che le _BIN2regole di confronto corrispondono in modo conveniente al comportamento Ordinaldell'opzione dell'enumerazione StringComparison , in modo che i confronti e l'ordinamento eseguiti nel codice .NET utilizzando tale opzione produrranno gli stessi risultati delle stesse operazioni eseguite in SQL Server (quando si utilizza le _BIN2regole di confronto, ovviamente).

  4. Per ragioni analoghe a quanto è stato appena affermato riguardo alle _BIN2regole di confronto, a meno che non si disponga di requisiti specifici per mantenere il comportamento di compatibilità con le versioni precedenti, è necessario utilizzare le regole di confronto di Windows e non quelle specifiche di SQL Server (ovvero quelle che iniziano con SQL_sono ora considerate kinda "sucky" ;-)).

  5. Quando si utilizzano i dati Unicode (ovvero stringa con prefisso No entrata in SQL Server dal codice dell'app in cui il tipo di dati è stato specificato come NCharo NVarChar), non vedo come l'utilizzo di una fascicolazione rispetto a un altro possa fare la differenza per l'inserimento o l'aggiornamento di un campo NCHARo NVARCHARstringa .

    Quando si utilizzano dati non Unicode, o si inserisce o si aggiorna in un campo non Unicode, le regole di confronto specifiche (database o campo) potrebbero svolgere un piccolo ruolo se tutti i caratteri inseriti / aggiornati devono essere tradotti o non sono mappabili (è che anche una parola?), come specificato dalla pagina di codice che è definita dal confronto. Naturalmente, questo potenziale problema si presenta ogni volta che si utilizzano dati o tipi di dati non Unicode e non è specifico a questo scenario di modifica delle regole di confronto del DB. Tale modifica avrà effetto sui valori letterali delle stringhe (che potrebbero essere già stati un problema se le regole di confronto del DB fossero diverse dalle regole di confronto del campo). Ma anche se non viene apportata alcuna modifica alle regole di confronto dei DB, i dati provenienti da altri DB o dall'esterno di SQL Server (qualsiasi codice client) possono contenere qualsiasi carattere e avere una particolare codifica.

  6. MOLTO IMPORTANTE!!! Quando si modificano le regole di confronto predefinite del database, le regole di confronto specificate per tutti i campi stringa esistenti in qualsiasi tabella esistente non cambieranno, ma tutti i nuovi campi avranno un confronto delle impostazioni predefinite del database (a meno che non vengano sovrascritte dalla COLLATEclausola). Ciò influirà sulle tue query in tre modi:

    1) In caso di domande ISCRIVITI su uno di questi campi esistenti a uno dei nuovi campi, verrà visualizzato un errore di mancata corrispondenza delle regole di confronto:

    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;

    Ritorna:

    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) I predicati / i filtri sui campi esistenti delle tabelle esistenti (impostati sulle regole di confronto predefinite precedenti) che si confrontano con valori letterali o variabili di stringa non si guasteranno, ma potrebbero sicuramente essere influenzati dal punto di vista delle prestazioni a causa della necessità che SQL Server debba equiparare le regole di confronto di entrambi i lati e convertendo automaticamente la stringa letterale o variabile nelle regole di confronto del campo. Abilita "Includi piano di esecuzione effettivo" (Control-M) e quindi esegui quanto segue (supponendo che tu abbia già eseguito le query mostrate sopra):

    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) E, parlando di conversioni implicite, nota come sia la stringa letterale (con una collazione implicita delle regole di confronto predefinite del database:) Latin1_General_BIN2che viene convertita, non il campo nella tabella. Qualche ipotesi sul fatto che questo filtro non faccia distinzione tra maiuscole e minuscole (la vecchia fascicolazione) o sensibile alla maiuscola (la nuova fascicolazione)? Esegui quanto segue per vedere:

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

    Ritorna:

    Col1
    ----
    a
    A

    D'oh! Non solo c'è un lieve (o forse più significativo?) Successo delle prestazioni per questa query a causa di CONVERT_IMPLICIT(), ma non si comporta nemmeno nel modo sensibile al maiuscolo / minuscolo desiderato.

    Ergo, se le regole di confronto vengono modificate su un DB che ha già tabelle, allora si influiscono sia sulle prestazioni che sulla funzionalità.

    Se il confronto viene impostato su un nuovo DB, Paul lo ha già spiegato spiegando come un confronto binario, sebbene veloce, probabilmente non ordinerà come ci si aspetterebbe o desiderare.


Va inoltre notato che è sempre possibile specificare regole di confronto per condizione. La clausola COLLATE può essere aggiunta alle WHEREcondizioni ORDER BYe quasi tutti i luoghi che accettano una stringa.

Esempio 1 (condizione WHERE):

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;

Ritorna:

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

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

Esempio 2 (ORDINA PER):

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;

Ritorna:

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

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

Esempio 3 (istruzione 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];

Ritorna:

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

{nothing}

Esempio 4 (associato al parametro di input della funzione):

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/

Ritorna:

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

Il valore UCS-2 di 55.356 è parzialmente corretto in quanto è il primo dei due valori nella "coppia surrogata". Ma a meno che non venga esplicitamente indicato il _SCconfronto, la UNICODE()funzione può solo vedere ogni carattere come un valore a doppio byte e non sa come gestire correttamente una coppia surrogata a doppio byte doppio.


AGGIORNARE

Anche con tutti gli esempi di cui sopra, un aspetto dei confronti con distinzione tra maiuscole e minuscole che di solito viene trascurato ed è negato da confronti / confronti binari, è la normalizzazione (composizione e decomposizione) che fa parte di Unicode.

Esempio 5 (quando un confronto binario non fa distinzione tra maiuscole e minuscole):

I veri confronti con distinzione tra maiuscole e minuscole consentono di combinare caratteri che, in combinazione con un altro carattere, formano ancora un altro carattere che esiste già come un altro punto di codice Unicode. I confronti con distinzione tra maiuscole e minuscole si preoccupano del carattere visualizzabile, non dei punti di codice utilizzati per 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

Ritorna:

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

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

I veri confronti con distinzione tra maiuscole e minuscole consentono inoltre ai caratteri ampi di equivalere ai loro equivalenti non ampi.

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

Ritorna:

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


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

Quindi:

Le raccolte BINARY ( _BINe _BIN2) non sono sensibili al maiuscolo / minuscolo!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.