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.
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 _BIN
oder 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?
Kollatierungen wirken sich nicht nur auf Vergleiche, sondern auch auf die Sortierung aus. Eine binäre Kollatierung wird basierend auf dem ASCII
oder dem UNICODE
Bytewert (abhängig von VARCHAR
bzw. 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?
Sofern Sie für Ihre Anwendung keine speziellen Abwärtskompatibilitätsanforderungen haben, sollten Sie die BIN2
anstelle von BIN
Kollatierungen verwenden, vorausgesetzt natürlich, Sie möchten zunächst eine binäre Kollatierung. Die BIN2
Kollatierungen 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 Ordinal
Option 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 _BIN2
Kollatierungen natürlich).
Aus ähnlichen Gründen wie oben in Bezug auf die _BIN2
Kollatierungen 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" ;-)).
Wenn Unicode - Daten unter Verwendung von (dh Zeichenfolge mit dem Präfix N
oder von App - Code in SQL Server kommen , wo der Datentyp wie angegeben wurde NChar
oder NVarChar
), sehe ich nicht , wie gegen eine andere Sortierung verwendet wird, einen Unterschied zum Einfügen oder Aktualisieren eines machen würde NCHAR
oder NVARCHAR
String - 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.
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 COLLATE
Klausel ü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_BIN2
konvertiert 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 WHERE
Bedingungen hinzugefügt werden, ORDER BY
und 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 _SC
Sortierung 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 ( _BIN
und _BIN2
) berücksichtigen nicht die Groß- und Kleinschreibung!