Jika Anda berurusan dengan NVARCHAR
/ NCHAR
data (yang disimpan sebagai UTF-16 Little Endian ), maka Anda akan menggunakan Unicode
penyandian, bukan BigEndianUnicode
. Dalam .NET, UTF-16 disebut Unicode
sementara penyandian Unicode lainnya dirujuk dengan nama sebenarnya:UTF7, UTF8, dan UTF32. Oleh karena itu, Unicode
dengan sendirinya adalah Little Endian
sebagai lawan dari BigEndianUnicode
. PERBARUI: Silakan lihat bagian di akhir tentang UCS-2 dan Karakter Tambahan.
Di sisi basis data:
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
Di sisi .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
Namun, pertanyaan ini berkaitan dengan VARCHAR
/ CHAR
data, yaitu ASCII, sehingga semuanya sedikit lebih rumit.
Di sisi basis data:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
Kita sudah melihat sisi .NET di atas. Dari nilai hash tersebut seharusnya ada dua pertanyaan:
- Mengapa tidak ada di antaranya cocok dengan
HASHBYTES
nilai? - Mengapa artikel "sqlteam.com" yang ditautkan dalam jawaban @Eric J. menunjukkan bahwa tiga di antaranya (
ASCII
,UTF7
, danUTF8
) semua cocok denganHASHBYTES
nilai?
Ada satu jawaban yang mencakup kedua pertanyaan:Halaman Kode. Pengujian yang dilakukan pada artikel "sqlteam" menggunakan karakter ASCII "aman" yang berada dalam rentang 0 - 127 (dalam hal nilai int/desimal) yang tidak berbeda antar Halaman Kode. Namun rentang 128 - 255 -- tempat kami menemukan karakter "è" -- adalah Diperpanjang set yang bervariasi menurut Halaman Kode (yang masuk akal karena ini adalah alasan untuk memiliki Halaman Kode).
Sekarang coba:
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
Itu cocok dengan ASCII
nilai hash (dan lagi, karena artikel / pengujian "sqlteam" menggunakan nilai dalam rentang 0 - 127, mereka tidak melihat perubahan apa pun saat menggunakan COLLATE
). Bagus, sekarang kami akhirnya menemukan cara untuk mencocokkan VARCHAR
/ CHAR
data. Semuanya baik-baik saja?
Yah, tidak juga. Mari kita lihat apa yang sebenarnya kita hashing:
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];
Pengembalian:
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
Sebuah ?
? Hanya untuk memverifikasi, jalankan:
SELECT CHAR(63) AS [WhatIs63?];
-- ?
Ah, jadi Halaman Kode 1255 tidak memiliki è
karakter, sehingga diterjemahkan sebagai ?
. Tapi mengapa itu cocok dengan nilai hash MD5 di .NET saat menggunakan pengkodean ASCII? Mungkinkah kita sebenarnya tidak cocok dengan nilai hash è
, tetapi malah mencocokkan nilai hash ?
:
SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
Ya. Kumpulan karakter ASCII yang sebenarnya adalah hanya 128 karakter pertama (nilai 0 - 127). Dan seperti yang baru saja kita lihat, è
adalah 232. Jadi, menggunakan ASCII
encoding di .NET tidak terlalu membantu. Juga tidak menggunakan COLLATE
di sisi T-SQL.
Apakah mungkin untuk mendapatkan pengkodean yang lebih baik di sisi .NET? Ya, dengan menggunakan Encoding.GetEncoding(Int32), yang memungkinkan untuk menentukan Halaman Kode. Halaman Kode yang akan digunakan dapat ditemukan menggunakan kueri berikut (gunakan sys.columns
saat bekerja dengan kolom alih-alih literal atau variabel):
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
Kueri di atas mengembalikan (untuk saya):
Latin1_General_100_CI_AS_SC 1252
Jadi, mari kita coba Halaman Kode 1252:
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
Woo hoo! Kami memiliki kecocokan untuk VARCHAR
data yang menggunakan susunan SQL Server default kami :). Tentu saja, jika data berasal dari database atau bidang yang disetel ke susunan yang berbeda, maka GetEncoding(1252)
mungkin tidak berfungsi dan Anda harus menemukan Halaman Kode yang sebenarnya cocok menggunakan kueri yang ditunjukkan di atas (Halaman Kode digunakan di banyak Kolasi, jadi Kolasi yang berbeda tidak harus menyiratkan Halaman Kode yang berbeda).
Untuk melihat kemungkinan nilai Halaman Kode, dan budaya / lokal apa yang terkait, silakan lihat daftar Halaman Kode di sini (daftar ada di bagian "Keterangan").
Info tambahan terkait dengan apa yang sebenarnya disimpan di NVARCHAR
/ NCHAR
bidang:
Setiap karakter UTF-16 (2 atau 4 byte) dapat disimpan, meskipun perilaku default dari fungsi bawaan mengasumsikan bahwa semua karakter adalah UCS-2 (masing-masing 2 byte), yang merupakan subset dari UTF-16. Mulai dari SQL Server 2012, dimungkinkan untuk mengakses sekumpulan kumpulan Windows yang mendukung karakter 4 byte yang dikenal sebagai Karakter Tambahan. Menggunakan salah satu dari kumpulan Windows ini yang diakhiri dengan _SC
, baik ditentukan untuk kolom atau langsung dalam kueri, akan memungkinkan fungsi bawaan menangani karakter 4 byte dengan benar.
-- 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];
Pengembalian:
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
Seperti yang Anda lihat, tidak ada DATALENGTH
atau HASHBYTES
terpengaruh. Untuk informasi lebih lanjut, silakan lihat halaman MSDN untuk Collation dan Dukungan Unicode (khususnya bagian "Karakter Tambahan").