Ако имате работа с NVARCHAR
/ NCHAR
данни (които се съхраняват като UTF-16 Little Endian ), тогава ще използвате Unicode
кодиране, а не BigEndianUnicode
. В .NET UTF-16 се нарича Unicode
докато други кодировки на Unicode се обозначават с действителните си имена:UTF7, UTF8 и UTF32. Следователно Unicode
сам по себе си е Little Endian
за разлика от BigEndianUnicode
. АКТУАЛИЗИРАНЕ: Моля, вижте раздела в края относно UCS-2 и допълнителните символи.
От страната на базата данни:
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
От страна на .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
Този въпрос обаче се отнася до VARCHAR
/ CHAR
данни, което е ASCII, така че нещата са малко по-сложни.
От страната на базата данни:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
Вече виждаме .NET страната по-горе. От тези хеширани стойности трябва да има два въпроса:
- Защо не някои от тях съответстват на
HASHBYTES
стойност? - Защо статията "sqlteam.com", свързана в отговора на @Eric J., показва, че три от тях (
ASCII
,UTF7
иUTF8
) всички съответстват наHASHBYTES
стойност?
Има един отговор, който покрива и двата въпроса:Кодови страници. Тестът, направен в статията "sqlteam", използва "безопасни" ASCII символи, които са в диапазона 0 - 127 (по отношение на int / десетичната стойност), които не варират между кодовите страници. Но диапазонът 128 - 255 - където намираме знака "è" - е Разширеният набор, който варира според кодовата страница (което има смисъл, тъй като това е причината за наличието на кодови страници).
Сега опитайте:
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
Това съвпада с ASCII
хеширана стойност (и отново, тъй като статията/тестът на "sqlteam" използва стойности в диапазона 0 - 127, те не виждат никакви промени при използване на COLLATE
). Чудесно, сега най-накрая намерихме начин да съпоставим VARCHAR
/ CHAR
данни. Всичко наред?
Е, не наистина. Нека да видим какво всъщност хеширахме:
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];
Връща:
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
A ?
? Само за да проверите, стартирайте:
SELECT CHAR(63) AS [WhatIs63?];
-- ?
А, значи кодова страница 1255 няма è
символ, така че се превежда като любимия на всички ?
. Но защо тогава това съответства на хешираната стойност MD5 в .NET при използване на ASCII кодиране? Възможно ли е всъщност да не съвпадаме с хешираната стойност на è
, но вместо това съответстваха на хешираната стойност на ?
:
SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
Мда. Истинският набор от ASCII символи е просто първите 128 знака (стойности 0 - 127). И както току-що видяхме, è
е 232. И така, използвайки ASCII
кодирането в .NET не е толкова полезно. Нито използва COLLATE
от страна на T-SQL.
Възможно ли е да се получи по-добро кодиране от страна на .NET? Да, чрез използване на Encoding.GetEncoding(Int32), което позволява да се посочи кодовата страница. Използваната кодова страница може да бъде открита чрез следната заявка (използвайте sys.columns
когато работите с колона вместо с литерал или променлива):
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
Заявката по-горе връща (за мен):
Latin1_General_100_CI_AS_SC 1252
И така, нека опитаме кодова страница 1252:
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
Ууууу! Имаме съвпадение за VARCHAR
данни, които използват нашето съпоставяне на SQL Server по подразбиране :). Разбира се, ако данните идват от база данни или поле, зададено на различно съпоставяне, тогава GetEncoding(1252)
може би не работи и ще трябва да намерите действително съответстващата кодова страница, като използвате заявката, показана по-горе (кодова страница се използва в много съпоставяния, така че различното съпоставяне не е непременно предполагат различна кодова страница).
За да видите какви са възможните стойности на кодовата страница и към каква култура / локал се отнасят, моля, вижте списъка с кодови страници тук (списъкът е в секцията „Забележки“).
Допълнителна информация, свързана с това, което всъщност се съхранява в NVARCHAR
/ NCHAR
полета:
Всеки UTF-16 символ (2 или 4 байта) може да бъде съхранен, въпреки че поведението по подразбиране на вградените функции предполага, че всички знаци са UCS-2 (по 2 байта всеки), което е подмножество на UTF-16. Започвайки от SQL Server 2012, е възможен достъп до набор от композиции на Windows, които поддържат 4-байтовите знаци, известни като допълнителни символи. Използване на едно от тези композиции на Windows, завършващи на _SC
, посочен или за колона, или директно в заявка, ще позволи на вградените функции да обработват правилно 4-байтовите знаци.
-- 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];
Връща:
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
Както можете да видите, нито DATALENGTH
нито HASHBYTES
са засегнати. За повече информация, моля, вижте страницата MSDN за поддръжка на сравняване и Unicode (по-специално секцията „Допълнителни знаци“).