Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

TSQL md5 băm khác với C # .NET md5

Nếu bạn đang xử lý NVARCHAR / NCHAR dữ liệu (được lưu trữ dưới dạng UTF-16 Little Endian ), thì bạn sẽ sử dụng Unicode mã hóa, không phải BigEndianUnicode . Trong .NET, UTF-16 được gọi là Unicode trong khi các bảng mã Unicode khác được gọi bằng tên thực của chúng:UTF7, UTF8 và UTF32. Do đó, Unicode tự nó là Little Endian trái ngược với BigEndianUnicode . CẬP NHẬT: Vui lòng xem phần ở cuối về UCS-2 và các ký tự bổ sung.

Về phía cơ sở dữ liệu:

SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF

Về phía .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

Tuy nhiên, câu hỏi này liên quan đến VARCHAR / CHAR dữ liệu, là ASCII và vì vậy mọi thứ phức tạp hơn một chút.

Về phía cơ sở dữ liệu:

SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934

Chúng ta đã thấy phía .NET ở trên. Từ các giá trị băm đó, có hai câu hỏi:

  • Tại sao không bất kỳ trong số chúng khớp với HASHBYTES giá trị?
  • Tại sao bài viết "sqlteam.com" được liên kết trong câu trả lời của @Eric J. cho thấy ba trong số đó (ASCII , UTF7UTF8 ) tất cả đều khớp với HASHBYTES giá trị?

Có một câu trả lời bao hàm cả hai câu hỏi:Trang Mã. Thử nghiệm được thực hiện trong bài viết "sqlteam" đã sử dụng các ký tự ASCII "an toàn" nằm trong phạm vi 0 - 127 (về giá trị int / thập phân) không khác nhau giữa các Trang mã. Nhưng phạm vi 128 - 255 - nơi chúng tôi tìm thấy ký tự "è" - là Mở rộng thiết lập thay đổi theo Trang mã (điều này có ý nghĩa vì đây là lý do để có Trang mã).

Bây giờ hãy thử:

SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D

Điều đó khớp với ASCII giá trị băm (và một lần nữa, vì bài viết / bài kiểm tra "sqlteam" đã sử dụng các giá trị trong phạm vi 0 - 127, họ không thấy bất kỳ thay đổi nào khi sử dụng COLLATE ). Tuyệt vời, cuối cùng chúng tôi đã tìm ra cách để đối sánh với VARCHAR / CHAR dữ liệu. Tất cả đều tốt?

Chà, không hẳn vậy. Hãy cùng xem những gì chúng tôi thực sự đã băm:

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];

Lợi nhuận:

TheChar TheASCIIvalue   CharCP1255  TheASCIIvalueCP1255
è       232             ?           63

Một ? ? Chỉ để xác minh, hãy chạy:

SELECT CHAR(63) AS [WhatIs63?];
-- ?

À, vậy là Trang Mã 1255 không có è , vì vậy nó được dịch là ? yêu thích của mọi người . Nhưng tại sao điều đó lại khớp với giá trị băm MD5 trong .NET khi sử dụng mã hóa ASCII? Có thể là chúng tôi không thực sự khớp với giá trị băm của è , nhưng thay vào đó khớp với giá trị băm của ? :

SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D

Chuẩn rồi. Bộ ký tự ASCII thực sự là just 128 ký tự đầu tiên (giá trị 0 - 127). Và như chúng ta vừa thấy, è là 232. Vì vậy, sử dụng ASCII mã hóa trong .NET không hữu ích. Cũng không sử dụng COLLATE về phía T-SQL.

Có thể nhận được mã hóa tốt hơn về phía .NET không? Có, bằng cách sử dụng Encoding.GetEncoding (Int32), cho phép chỉ định Trang mã. Trang mã để sử dụng có thể được khám phá bằng cách sử dụng truy vấn sau (sử dụng sys.columns khi làm việc với một cột thay vì một ký tự hoặc một biến):

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

Truy vấn ở trên trả về (đối với tôi):

Latin1_General_100_CI_AS_SC    1252

Vì vậy, hãy thử Mã Trang 1252:

System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934

Tuyệt vời! Chúng tôi có một kết quả phù hợp cho VARCHAR dữ liệu sử dụng đối chiếu SQL Server mặc định của chúng tôi :). Tất nhiên, nếu dữ liệu đến từ cơ sở dữ liệu hoặc trường được đặt thành đối chiếu khác, thì GetEncoding(1252) might không hoạt động và bạn sẽ phải tìm Trang mã phù hợp thực tế bằng cách sử dụng truy vấn được hiển thị ở trên (một Trang mã được sử dụng trên nhiều Đối tượng ghép, vì vậy một Đối chiếu khác không nhất thiết phải ngụ ý một Trang mã khác).

Để xem các giá trị có thể có của Trang Mã là gì và chúng liên quan đến văn hóa / địa phương nào, vui lòng xem danh sách các Trang Mã tại đây (danh sách nằm trong phần "Ghi chú").

Thông tin bổ sung liên quan đến những gì thực sự được lưu trữ trong NVARCHAR / NCHAR lĩnh vực:

Bất kỳ ký tự UTF-16 nào (2 hoặc 4 byte) đều có thể được lưu trữ, mặc dù hành vi mặc định của các hàm tích hợp giả định rằng tất cả các ký tự đều là UCS-2 (mỗi ký tự 2 byte), là một tập con của UTF-16. Bắt đầu từ SQL Server 2012, có thể truy cập một tập hợp các đối chiếu Windows hỗ trợ các ký tự 4 byte được gọi là Ký tự bổ sung. Sử dụng một trong các đối chiếu Windows này kết thúc bằng _SC , được chỉ định cho một cột hoặc trực tiếp trong một truy vấn, sẽ cho phép các hàm tích hợp xử lý đúng các ký tự 4 byte.

-- 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];

Lợi nhuận:

SupplementaryChar   LEN   DATALENGTH   UNICODE   LEFT   HASHBYTES
𨝫                  2     4             55393    �     0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫                  1     4            165739    𨝫     0x7A04F43DA81E3150F539C6B99F4B8FA9

Như bạn có thể thấy, cả DATALENGTH cũng không phải HASHBYTES bị ảnh hưởng. Để biết thêm thông tin, vui lòng xem trang MSDN về Hỗ trợ đối chiếu và Unicode (cụ thể là phần "Ký tự bổ sung").



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách tạo khóa ngoại trong SQL Server (Ví dụ T-SQL)

  2. SQL Server Express so với express localdb

  3. Ngày / Dấu thời gian để ghi khi một bản ghi được thêm vào bảng?

  4. Sự khác biệt giữa sys.columns, sys.system_columns và sys.all_columns trong SQL Server

  5. Cách thực hiện một thủ tục được lưu trữ trong chương trình C #