Étant donné qu'il s'agit d'une base de données existante qui contient déjà des tables définies, il y a des implications très sérieuses à l'action de changer le classement de la base de données, au-delà de l'impact potentiel sur les performances des ouvertures DML (qui était déjà là). Il y a un impact très réel sur les performances et les fonctionnalités, et ce changement non seulement n'a pas atteint l'objectif prévu (du moins pas systématiquement), mais a tout aussi probablement modifié le comportement (ou modifiera le comportement lors de la création de nouvelles tables) en termes de comment les données sont ordonnées et assimilées.
Paul a déjà fourni une bonne explication et des exemples des différences de performances et de comportement entre les différents types de classements dans sa réponse, donc je ne répéterai pas cela ici. Cependant, quelques points nécessitent des détails supplémentaires, et il y a plusieurs autres points à ajouter par rapport au scénario actuel de modification du classement d'une base de données existante, par opposition à la définition du classement d'une nouvelle base de données.
Les classements binaires sont plus que sensibles à la casse: ils sont tout sensibles! Ainsi, en utilisant un classement binaire (se terminant par _BIN
ou _BIN2
), vos comparaisons sont désormais également sensibles à l'accent, sensible au kana, sensible à la largeur et potentiellement sensible au gluten (du moins, cela semble être la tendance de nos jours ;-)). Était-ce l'effet souhaité de ce changement? Les utilisateurs finaux attendent-ils ce changement de comportement?
Les classements affectent non seulement les comparaisons, mais aussi le tri. Un classement binaire sera trié en fonction de la valeur d'octet ASCII
ou UNICODE
(selon VARCHAR
ou NVARCHAR
, respectivement) de chaque octet . Par conséquent, en choisissant un classement binaire, vous abandonnez les règles de pondération spécifiques à la langue / à la culture qui ordonnent chaque caractère (même les caractères dans une langue, comme le hongrois, qui sont composés de 2 lettres) selon l'alphabet de cette culture. Donc, si "ch" doit naturellement venir après "k", eh bien, cela ne se produira pas en utilisant un classement binaire. Encore une fois, était-ce l'effet souhaité de ce changement? Les utilisateurs finaux attendent-ils ce changement de comportement?
Sauf si vous avez des exigences de compatibilité descendante spécifiques pour votre application, vous devez utiliser le BIN2
au lieu des BIN
classements, en supposant, bien sûr, que vous souhaitez un classement binaire en premier lieu. Les BIN2
classements ont été introduits dans SQL Server 2005 et selon la page MSDN pour les directives d'utilisation des classements BIN et BIN2 :
Les classements binaires précédents dans SQL Server, ceux se terminant par "_BIN", ont effectué une comparaison de point de code à point de code incomplète pour les données Unicode. Les classements binaires SQL Server plus anciens comparaient le premier caractère en tant que WCHAR, suivi d'une comparaison octet par octet.
...
Vous pouvez migrer vers les classements binaires [_BIN2] pour tirer parti de véritables comparaisons de points de code, et vous devez utiliser les nouveaux classements binaires pour le développement de nouvelles applications.
Il convient également de noter que les _BIN2
classements correspondent commodément au comportement de l' Ordinal
option de l' énumération StringComparison , de sorte que les comparaisons et le tri effectués dans le code .NET à l'aide de cette option produiront les mêmes résultats que ces mêmes opérations effectuées dans SQL Server (lors de l'utilisation de les _BIN2
classements, bien sûr).
Pour des raisons similaires à ce qui vient d'être indiqué concernant les _BIN2
classements, à moins que vous n'ayez des exigences spécifiques pour maintenir un comportement de compatibilité descendante, vous devriez vous pencher vers l'utilisation des classements Windows et non des classements spécifiques à SQL Server (c'est-à-dire que ceux commençant par SQL_
sont maintenant considérés un peu "nul" ;-)).
Lorsque j'utilise des données Unicode (c'est-à-dire une chaîne préfixée avec N
ou venant dans SQL Server à partir du code d'application où le type de données a été spécifié comme NChar
ou NVarChar
), je ne vois pas comment l'utilisation d'un classement par rapport à un autre ferait une différence pour l'insertion ou la mise à jour d'un champ de chaîne NCHAR
ou NVARCHAR
.
Lorsque vous utilisez des données non Unicode ou que vous insérez ou mettez à jour un champ non Unicode, le classement particulier (base de données ou champ) peut jouer un petit rôle si des caractères insérés / mis à jour doivent être traduits ou ne sont pas mappables (est que même un mot?), comme spécifié par la page de codes définie par le classement. Bien sûr, ce problème potentiel existe chaque fois que l'on utilise des données ou des types de données non Unicode et n'est pas spécifique à ce scénario de modification du classement de base de données. Cette modification affectera les littéraux de chaîne (ce qui pourrait déjà avoir été un problème si le classement de la base de données était différent du classement du champ). Mais même si aucune modification n'est apportée au classement des bases de données, les données provenant d'autres bases de données ou de l'extérieur de SQL Server (n'importe quel code client) peuvent contenir des caractères et avoir un codage particulier.
TRÈS IMPORTANT!!! Lorsque vous modifiez le classement par défaut de la base de données, le classement spécifié pour tous les champs de chaîne existants dans les tables existantes ne changera pas , mais tous les nouveaux champs auront un classement de la base de données par défaut (à moins qu'il ne soit remplacé par la COLLATE
clause). Cela aura un impact sur vos requêtes de trois manières:
1) Si des requêtes JOIN sur l'un de ces champs existants à l'un des nouveaux champs, vous obtiendrez une erreur de non-concordance de classement:
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;
Retour:
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) Les prédicats / filtres sur les champs existants des tables existantes (définis sur le classement par défaut précédent) qui se comparent aux littéraux de chaîne ou aux variables n'erreront pas, mais ils pourraient certainement être affectés en termes de performances car SQL Server doit assimiler le classement de des deux côtés et la conversion automatique du littéral de chaîne ou de la variable au classement du champ. Activez «Inclure le plan d'exécution réel» (Control-M), puis exécutez ce qui suit (en supposant que vous avez déjà exécuté les requêtes ci-dessus):
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) ET, en parlant de conversions implicites, notez comment c'est le littéral de chaîne (avec un classement implicite du classement par défaut de la base de données:) Latin1_General_BIN2
qui est converti, pas le champ dans la table. Est-ce que vous devinez si ce filtre sera insensible à la casse (l'ancien classement) ou sensible à la casse (le nouveau classement)? Exécutez ce qui suit pour voir:
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';
Retour:
Col1
----
a
A
Oh! Non seulement y a-t-il un léger (ou peut-être plus significatif?) Coup de performances pour cette requête en raison de la CONVERT_IMPLICIT()
, mais il ne se comporte même pas de la manière souhaitée respectant la casse.
Ergo, si le classement est modifié sur une base de données qui possède déjà des tables, alors oui, les performances ET les fonctionnalités sont affectées.
Si le classement est défini sur une nouvelle base de données, Paul a déjà couvert cela en expliquant comment un classement binaire, bien que rapide, ne triera probablement pas de la manière attendue ou souhaitée.
Il convient également de noter que vous pouvez toujours spécifier des classements par condition. La clause COLLATE peut être ajoutée aux WHERE
conditions ORDER BY
, et à la plupart des emplacements acceptant une chaîne.
Exemple 1 (où condition):
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;
Retour:
SQL-CaseSensitive
-----------------
b
B
Windows-CaseSensitive
-----------------
A
b
B
Exemple 2 (COMMANDER PAR):
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;
Retour:
Windows-CaseSensitive
-----------------
a
A
b
B
Windows-Binary
-----------------
A
B
a
b
Exemple 3 (instruction 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];
Retour:
DatabaseDefault-CaseInsensitive?
--------------------------------
1
{nothing}
Exemple 4 (associé au paramètre d'entrée de fonction):
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/
Retour:
UCS-2 UTF-16
------ -------
55356 127137
La valeur UCS-2 de 55 356 est partiellement correcte en ce qu'elle est la première des deux valeurs de la "paire de substitution". Mais sauf indication explicite du _SC
classement, la UNICODE()
fonction ne peut voir chaque caractère que comme une valeur à deux octets et ne sait pas comment gérer correctement une paire de substitution à deux octets.
MISE À JOUR
Même avec tous les exemples ci-dessus, un aspect des comparaisons sensibles à la casse qui est généralement ignoré et annulé par les comparaisons / collations binaires est la normalisation (composition et décomposition) qui fait partie d'Unicode.
Exemple 5 (lorsqu'une comparaison binaire n'est pas sensible à la casse):
De vraies comparaisons sensibles à la casse permettent de combiner des caractères qui, en combinaison avec un autre caractère, forment encore un autre caractère qui existe déjà comme un autre point de code Unicode. Les comparaisons sensibles à la casse se soucient du caractère affichable, pas du ou des points de code utilisés pour le créer.
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
Retour:
Binary ü u + combining diaeresis
------- --- -------------------------
{nothing}
Case-Sensitive ü u + combining diaeresis
--------------- --- -------------------------
Equal ü ü
De vraies comparaisons sensibles à la casse permettent également aux caractères larges d'être équivalents à leurs équivalents non larges.
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];
Retour:
Binary
---------------
Values are different
Case-Sensitive
---------------
Values are the same
Ergo:
Les classements BINARY ( _BIN
et _BIN2
) ne sont pas sensibles à la casse!