Si vous avez affaire à NVARCHAR
/ NCHAR
données (qui sont stockées en tant que UTF-16 Little Endian ), alors vous utiliseriez le Unicode
encodage, pas BigEndianUnicode
. Dans .NET, UTF-16 est appelé Unicode
tandis que les autres encodages Unicode sont désignés par leurs noms réels :UTF7, UTF8 et UTF32. Par conséquent, Unicode
par lui-même est Little Endian
par opposition à BigEndianUnicode
. MISE À JOUR : Veuillez consulter la section à la fin concernant le SCU-2 et les caractères supplémentaires.
Côté base de données :
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
Côté .NET :
System.Text.Encoding.ASCII.GetBytes("è")
// D1457B72C3FB323A2671125AEF3EAB5D
System.Text.Encoding.UTF7.GetBytes("è")
// F63A0999FE759C5054613DDE20346193
System.Text.Encoding.UTF8.GetBytes("è")
// 0A35E149DBBB2D10D744BF675C7744B1
System.Text.Encoding.UTF32.GetBytes("è")
// 86D29922AC56CF022B639187828137F8
System.Text.Encoding.BigEndianUnicode.GetBytes("è")
// 407256AC97E4C5AEBCA825DEB3D2E89C
System.Text.Encoding.Unicode.GetBytes("è") // this one matches HASHBYTES('MD5', N'è')
// FAC02CD988801F0495D35611223782CF
Cependant, cette question concerne VARCHAR
/ CHAR
data, qui est ASCII, et donc les choses sont un peu plus compliquées.
Côté base de données :
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
On voit déjà le côté .NET ci-dessus. À partir de ces valeurs hachées, il devrait y avoir deux questions :
- Pourquoi n'en faire aucun d'entre eux correspondent aux
HASHBYTES
valeur ? - Pourquoi l'article "sqlteam.com" lié dans la réponse de @Eric J. montre-t-il que trois d'entre eux (
ASCII
,UTF7
, etUTF8
) correspondent tous auHASHBYTES
valeur ?
Il existe une réponse qui couvre les deux questions :les pages de code. Le test effectué dans l'article "sqlteam" a utilisé des caractères ASCII "sûrs" compris entre 0 et 127 (en termes de valeur int/decimal) qui ne varient pas entre les pages de code. Mais la plage 128 - 255 -- où l'on trouve le caractère "è" -- est la Extended ensemble qui varie selon la page de code (ce qui est logique car c'est la raison d'avoir des pages de code).
Essayez maintenant :
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
Qui correspond au ASCII
valeur hachée (et encore une fois, parce que l'article / le test "sqlteam" utilisait des valeurs comprises entre 0 et 127, ils n'ont vu aucun changement lors de l'utilisation de COLLATE
). Super, maintenant nous avons enfin trouvé un moyen de faire correspondre VARCHAR
/ CHAR
Les données. Tout va bien ?
Eh bien pas vraiment. Jetons un coup d'œil à ce que nous étions en train de hacher :
SELECT 'è' AS [TheChar],
ASCII('è') AS [TheASCIIvalue],
'è' COLLATE SQL_Latin1_General_CP1255_CI_AS AS [CharCP1255],
ASCII('è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [TheASCIIvalueCP1255];
Renvoie :
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
Un ?
? Juste pour vérifier, exécutez :
SELECT CHAR(63) AS [WhatIs63?];
-- ?
Ah, donc la page de code 1255 n'a pas le è
caractère, donc il est traduit par le ?
préféré de tout le monde . Mais alors pourquoi cela correspond-il à la valeur hachée MD5 dans .NET lors de l'utilisation de l'encodage ASCII ? Se pourrait-il que nous ne correspondions pas réellement à la valeur hachée de è
, mais correspondaient à la place à la valeur hachée de ?
:
SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
Ouais. Le vrai jeu de caractères ASCII est juste les 128 premiers caractères (valeurs 0 - 127). Et comme nous venons de le voir, le è
est 232. Donc, en utilisant le ASCII
l'encodage dans .NET n'est pas très utile. N'utilisait pas non plus COLLATE
du côté T-SQL.
Est-il possible d'obtenir un meilleur encodage côté .NET ? Oui, en utilisant Encoding.GetEncoding(Int32), qui permet de spécifier la page de code. La page de code à utiliser peut être découverte à l'aide de la requête suivante (utilisez sys.columns
lorsque vous travaillez avec une colonne au lieu d'un littéral ou d'une variable) :
SELECT sd.[collation_name],
COLLATIONPROPERTY(sd.[collation_name], 'CodePage') AS [CodePage]
FROM sys.databases sd
WHERE sd.[name] = DB_NAME(); -- replace function with N'{db_name}' if not running in the DB
La requête ci-dessus renvoie (pour moi) :
Latin1_General_100_CI_AS_SC 1252
Alors, essayons la page de code 1252 :
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
Woo hoo! Nous avons une correspondance pour VARCHAR
données qui utilisent notre collation SQL Server par défaut :). Bien sûr, si les données proviennent d'une base de données ou d'un champ défini sur un classement différent, alors GetEncoding(1252)
pourrait ne fonctionne pas et vous devrez trouver la page de code correspondante à l'aide de la requête ci-dessus (une page de code est utilisée dans de nombreux classements, donc un autre classement ne le fait pas nécessairement impliquent une page de code différente).
Pour voir quelles sont les valeurs possibles de la page de code et à quelle culture/région elles se rapportent, veuillez consulter la liste des pages de code ici (la liste se trouve dans la section "Remarques").
Informations supplémentaires liées à ce qui est réellement stocké dans NVARCHAR
/ NCHAR
champs :
Tout caractère UTF-16 (2 ou 4 octets) peut être stocké, bien que le comportement par défaut des fonctions intégrées suppose que tous les caractères sont UCS-2 (2 octets chacun), qui est un sous-ensemble de UTF-16. À partir de SQL Server 2012, il est possible d'accéder à un ensemble de classements Windows prenant en charge les caractères de 4 octets appelés caractères supplémentaires. Utiliser l'un de ces classements Windows se terminant par _SC
, spécifié pour une colonne ou directement dans une requête, permettra aux fonctions intégrées de gérer correctement les caractères de 4 octets.
-- The database's collation is set to: SQL_Latin1_General_CP1_CI_AS
SELECT N'𨝫' AS [SupplementaryCharacter],
LEN(N'𨝫') AS [LEN],
DATALENGTH(N'𨝫') AS [DATALENGTH],
UNICODE(N'𨝫') AS [UNICODE],
LEFT(N'𨝫', 1) AS [LEFT],
HASHBYTES('MD5', N'𨝫') AS [HASHBYTES];
SELECT N'𨝫' AS [SupplementaryCharacter],
LEN(N'𨝫' COLLATE Latin1_General_100_CI_AS_SC) AS [LEN],
DATALENGTH(N'𨝫' COLLATE Latin1_General_100_CI_AS_SC) AS [DATALENGTH],
UNICODE(N'𨝫' COLLATE Latin1_General_100_CI_AS_SC) AS [UNICODE],
LEFT(N'𨝫' COLLATE Latin1_General_100_CI_AS_SC, 1) AS [LEFT],
HASHBYTES('MD5', N'𨝫' COLLATE Latin1_General_100_CI_AS_SC) AS [HASHBYTES];
Renvoie :
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
Comme vous pouvez le voir, ni DATALENGTH
ni HASHBYTES
sont affectés. Pour plus d'informations, veuillez consulter la page MSDN pour le classement et la prise en charge d'Unicode (en particulier la section "Caractères supplémentaires").