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.
Le regole di confronto binarie non sono solo sensibili al maiuscolo / minuscolo: sono tutto sensibile! Quindi, usando una fascicolazione binaria (che termina con _BIN
o _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?
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 ASCII
o UNICODE
(in base a , VARCHAR
o 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?
A meno che non si disponga di specifici requisiti di compatibilità con le versioni precedenti per l'applicazione, è consigliabile utilizzare le regole BIN2
di BIN
confronto anziché quelle , presupponendo, ovviamente, che si desideri innanzitutto una raccolta binaria. Le BIN2
regole 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 _BIN2
regole di confronto corrispondono in modo conveniente al comportamento Ordinal
dell'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 _BIN2
regole di confronto, ovviamente).
Per ragioni analoghe a quanto è stato appena affermato riguardo alle _BIN2
regole 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" ;-)).
Quando si utilizzano i dati Unicode (ovvero stringa con prefisso N
o entrata in SQL Server dal codice dell'app in cui il tipo di dati è stato specificato come NChar
o 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 NCHAR
o NVARCHAR
stringa .
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.
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 COLLATE
clausola). 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_BIN2
che 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 WHERE
condizioni ORDER BY
e 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 _SC
confronto, 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 ( _BIN
e _BIN2
) non sono sensibili al maiuscolo / minuscolo!