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
,UTF7
vàUTF8
) tất cả đều khớp vớiHASHBYTES
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").