Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

TSQL md5 хеш, различен от C# .NET md5

Ако имате работа с 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 (по-специално секцията „Допълнителни знаци“).



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Импортирайте CSV файл в SQL Server

  2. SQL:Актуализиране на ред и връщане на стойност на колона с 1 заявка

  3. CLR строга сигурност на SQL Server 2017

  4. 2 начина да видите дали остарелите функции все още се използват в екземпляр на SQL сървър

  5. Намерете понеделници между 2 дати