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í
HASHBYTEShodnotu? - Proč článek „sqlteam.com“ odkazovaný v odpovědi @Erica J. ukazuje, že tři z nich (
ASCII,UTF7aUTF8) všechny odpovídajíHASHBYTEShodnotu?
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").