Om du har att göra med NVARCHAR
/ NCHAR
data (som lagras som UTF-16 Little Endian ), då skulle du använda Unicode
kodning, inte BigEndianUnicode
. I .NET kallas UTF-16 för Unicode
medan andra Unicode-kodningar hänvisas till med sina faktiska namn:UTF7, UTF8 och UTF32. Därför Unicode
i sig är Little Endian
i motsats till BigEndianUnicode
. UPPDATERING: Se avsnittet i slutet om UCS-2 och tilläggstecken.
På databassidan:
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
På .NET-sidan:
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
Den här frågan hänför sig dock till VARCHAR
/ CHAR
data, som är ASCII, så saker och ting är lite mer komplicerade.
På databassidan:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
Vi ser redan .NET-sidan ovan. Från dessa hashade värden bör det finnas två frågor:
- Varför inte någon av dem matchar
HASHBYTES
värde? - Varför visar artikeln "sqlteam.com" som är länkad i @Eric J:s svar att tre av dem (
ASCII
,UTF7
ochUTF8
) matchar allaHASHBYTES
värde?
Det finns ett svar som täcker båda frågorna:Kodsidor. Testet som gjordes i "sqlteam"-artikeln använde "säkra" ASCII-tecken som ligger i intervallet 0 - 127 (i termer av int / decimalvärdet) som inte varierar mellan kodsidor. Men intervallet 128 - 255 -- där vi hittar tecknet "è" -- är den Utökade uppsättning som varierar beroende på kodsida (vilket är vettigt eftersom detta är anledningen till att ha kodsidor).
Försök nu:
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
Det matchar ASCII
hashat värde (och igen, eftersom "sqlteam"-artikeln / -testet använde värden i intervallet 0 - 127, såg de inga ändringar när de använde COLLATE
). Bra, nu har vi äntligen hittat ett sätt att matcha VARCHAR
/ CHAR
data. Allt bra?
Tja, inte riktigt. Låt oss ta en titt och se vad vi faktiskt hashade:
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];
Returnerar:
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
En ?
? Bara för att verifiera, kör:
SELECT CHAR(63) AS [WhatIs63?];
-- ?
Ah, så Code Page 1255 har inte è
tecken, så det översätts som allas favorit ?
. Men varför matchade det då MD5-hashvärdet i .NET när man använder ASCII-kodning? Kan det vara så att vi faktiskt inte matchade det hashade värdet för è
, men i stället matchade det hashade värdet för ?
:
SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
Japp. Den sanna ASCII-teckenuppsättningen är bara de första 128 tecknen (värden 0 - 127). Och som vi precis såg, è
är 232. Så, med ASCII
kodning i .NET är inte så användbart. Inte heller använde COLLATE
på T-SQL-sidan.
Är det möjligt att få en bättre kodning på .NET-sidan? Ja, genom att använda Encoding.GetEncoding(Int32), som gör det möjligt att ange kodsidan. Kodsidan som ska användas kan hittas med hjälp av följande fråga (använd sys.columns
när du arbetar med en kolumn istället för en bokstavlig eller variabel):
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
Frågan ovan returnerar (för mig):
Latin1_General_100_CI_AS_SC 1252
Så låt oss prova Code Page 1252:
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
Woo ho! Vi har en matchning för VARCHAR
data som använder vår standard SQL Server-sortering :). Naturligtvis, om data kommer från en databas eller ett fält som är inställt på en annan sortering, GetEncoding(1252)
kanske inte fungerar och du måste hitta den faktiska matchande kodsidan med hjälp av frågan som visas ovan (en kodsida används över många sorteringar, så en annan sortering behöver inte nödvändigtvis innebär en annan kodsida).
För att se vilka de möjliga kodsidorna är, och vilken kultur/lokal de tillhör, se listan över kodsidor här (listan finns i avsnittet "Anmärkningar").
Ytterligare information relaterad till vad som faktiskt lagras i NVARCHAR
/ NCHAR
fält:
Alla UTF-16-tecken (2 eller 4 byte) kan lagras, även om standardbeteendet för de inbyggda funktionerna förutsätter att alla tecken är UCS-2 (2 byte vardera), vilket är en delmängd av UTF-16. Från och med SQL Server 2012 är det möjligt att komma åt en uppsättning Windows-kollationer som stöder de 4 byte-tecken som kallas tilläggstecken. Använder en av dessa Windows-kollationer som slutar på _SC
, antingen specificerad för en kolumn eller direkt i en fråga, gör det möjligt för de inbyggda funktionerna att korrekt hantera de 4 byte tecknen.
-- 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];
Returnerar:
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
Som du kan se, varken DATALENGTH
inte heller HASHBYTES
Är påverkade. För mer information, se MSDN-sidan för Collation och Unicode Support (särskilt avsnittet "Supplerande tecken").