Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

TSQL md5 hash berbeda dengan C# .NET md5

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 , dan UTF8 ) semua cocok dengan HASHBYTES 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").



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cari Tahu apakah Partisi Dikompresi di SQL Server (T-SQL)

  2. Bagaimana menemukan kueri berkinerja terburuk di SQL Server 2008?

  3. Konversi 'datetime2' menjadi 'waktu' di SQL Server (Contoh T-SQL)

  4. Periksa/Ubah Tingkat Kompatibilitas Database di SQL Server (SSMS)

  5. Mengganti Partisi Tabel di SQL Server:Sebuah Panduan