Pokud máte co do činění s NVARCHAR
/ NCHAR
data (která jsou uložena jako UTF-16 Little Endian ), pak byste použili Unicode
kódování, nikoli BigEndianUnicode
. V .NET se UTF-16 nazývá Unicode
zatímco ostatní kódování Unicode jsou označována svými skutečnými názvy:UTF7, UTF8 a UTF32. Proto Unicode
sám o sobě je Little Endian
na rozdíl od BigEndianUnicode
. AKTUALIZACE: Viz část na konci týkající se UCS-2 a doplňkových znaků.
Na straně databáze:
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
Na straně .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
Tato otázka se však týká VARCHAR
/ CHAR
data, což je ASCII, takže věci jsou trochu složitější.
Na straně databáze:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
Strana .NET již vidíme výše. Z těchto hashovaných hodnot by měly být dvě otázky:
- Proč ne žádné z nich odpovídají
HASHBYTES
hodnotu? - Proč článek „sqlteam.com“ odkazovaný v odpovědi @Erica J. ukazuje, že tři z nich (
ASCII
,UTF7
aUTF8
) všechny odpovídajíHASHBYTES
hodnotu?
Existuje jedna odpověď, která pokrývá obě otázky:Kódové stránky. Test provedený v článku "sqlteam" použil "bezpečné" znaky ASCII, které jsou v rozsahu 0 - 127 (ve smyslu hodnoty int / desítkové), které se mezi kódovými stránkami neliší. Ale rozsah 128–255 – kde najdeme znak „è“ – je Rozšířený sada, která se liší podle kódové stránky (což dává smysl, protože to je důvod, proč mít kódové stránky).
Nyní zkuste:
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
To odpovídá ASCII
hashovaná hodnota (a znovu, protože článek / test "sqlteam" používal hodnoty v rozsahu 0 - 127, nezaznamenali při použití COLLATE
žádné změny ). Skvělé, nyní jsme konečně našli způsob, jak najít shodu s VARCHAR
/ CHAR
data. Všechno v pořádku?
No ne tak úplně. Pojďme se podívat, co jsme vlastně hashovali:
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];
Vrátí:
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
?
? Pro ověření spusťte:
SELECT CHAR(63) AS [WhatIs63?];
-- ?
Aha, takže kódová stránka 1255 nemá è
znak, takže bude přeložen jako oblíbený ?
. Ale proč se to shodovalo s hodnotou hash MD5 v .NET při použití kódování ASCII? Je možné, že jsme ve skutečnosti neodpovídali hashované hodnotě è
, ale místo toho odpovídaly hashované hodnotě ?
:
SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
Ano. Skutečná znaková sada ASCII je jen prvních 128 znaků (hodnoty 0 - 127). A jak jsme právě viděli, è
je 232. Tedy pomocí ASCII
kódování v .NET není tak užitečné. Nepoužil jste ani COLLATE
na straně T-SQL.
Je možné získat lepší kódování na straně .NET? Ano, pomocí Encoding.GetEncoding(Int32), který umožňuje specifikovat kódovou stránku. Kódovou stránku, kterou chcete použít, lze zjistit pomocí následujícího dotazu (použijte sys.columns
při práci se sloupcem místo literálu nebo proměnné):
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
Výše uvedený dotaz vrací (pro mě):
Latin1_General_100_CI_AS_SC 1252
Takže zkusme Kódovou stránku 1252:
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
Hurááá! Máme shodu pro VARCHAR
data, která používají naše výchozí řazení SQL Server :). Samozřejmě, pokud data pocházejí z databáze nebo pole nastaveného na jiné řazení, pak GetEncoding(1252)
možná nefunguje a budete muset najít skutečnou odpovídající kódovou stránku pomocí dotazu uvedeného výše (kódová stránka se používá v mnoha řazeních, takže jiné řazení nemusí implikovat jinou kódovou stránku).
Chcete-li zjistit, jaké jsou možné hodnoty kódové stránky a jaké kultury / národního prostředí se týkají, prohlédněte si seznam kódových stránek zde (seznam je v sekci "Poznámky").
Další informace související s tím, co je skutečně uloženo v NVARCHAR
/ NCHAR
pole:
Lze uložit jakýkoli znak UTF-16 (2 nebo 4 bajty), i když výchozí chování vestavěných funkcí předpokládá, že všechny znaky jsou UCS-2 (každý 2 bajty), což je podmnožina UTF-16. Počínaje SQL Serverem 2012 je možné přistupovat k sadě kolací systému Windows, které podporují 4bajtové znaky známé jako doplňkové znaky. Pomocí jednoho z těchto řazení Windows končících na _SC
, buď zadaný pro sloupec nebo přímo v dotazu, umožní vestavěným funkcím správně zpracovat 4bajtové znaky.
-- 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];
Vrátí:
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
Jak vidíte, ani DATALENGTH
ani HASHBYTES
jsou ovlivněny. Další informace naleznete na stránce MSDN pro podporu řazení a Unicode (konkrétně v sekci "Doplňkové znaky").