Wenn Sie es mit NVARCHAR
zu tun haben / NCHAR
Daten (die als UTF-16 Little Endian gespeichert werden ), dann würden Sie den Unicode
verwenden Codierung, nicht BigEndianUnicode
. In .NET heißt UTF-16 Unicode
während andere Unicode-Codierungen mit ihren tatsächlichen Namen bezeichnet werden:UTF7, UTF8 und UTF32. Daher Unicode
an sich ist Little Endian
im Gegensatz zu BigEndianUnicode
. AKTUALISIERUNG: Bitte lesen Sie den Abschnitt am Ende zu UCS-2 und ergänzenden Zeichen.
Auf der Datenbankseite:
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
Auf der .NET-Seite:
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
Diese Frage bezieht sich jedoch auf VARCHAR
/ CHAR
Daten, die ASCII sind, und daher sind die Dinge etwas komplizierter.
Auf der Datenbankseite:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
Oben sehen wir bereits die .NET-Seite. Aus diesen gehashten Werten sollten sich zwei Fragen ergeben:
- Warum nicht keine davon stimmen mit den
HASHBYTES
überein Wert? - Warum zeigt der in der Antwort von @Eric J. verlinkte "sqlteam.com"-Artikel, dass drei von ihnen (
ASCII
,UTF7
undUTF8
) stimmen alle mit denHASHBYTES
überein Wert?
Es gibt eine Antwort, die beide Fragen abdeckt:Codepages. Der im Artikel „sqlteam“ durchgeführte Test verwendete „sichere“ ASCII-Zeichen im Bereich von 0 bis 127 (in Bezug auf den Int-/Dezimalwert), die sich nicht zwischen den Codepages unterscheiden. Aber der Bereich von 128 bis 255 – wo wir das „è“-Zeichen finden – ist der Erweiterte Satz, der je nach Codepage variiert (was sinnvoll ist, da dies der Grund für Codepages ist).
Versuchen Sie jetzt:
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
Das entspricht dem ASCII
gehashter Wert (und noch einmal, weil der "sqlteam"-Artikel/Test Werte im Bereich von 0 - 127 verwendete, sahen sie keine Änderungen bei der Verwendung von COLLATE
). Großartig, jetzt haben wir endlich einen Weg gefunden, VARCHAR
abzugleichen / CHAR
Daten. Alles gut?
Nicht wirklich. Werfen wir einen Blick darauf, was wir tatsächlich gehasht haben:
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];
Rückgabe:
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
Ein ?
? Führen Sie einfach zur Überprüfung Folgendes aus:
SELECT CHAR(63) AS [WhatIs63?];
-- ?
Ah, Codepage 1255 hat also nicht den è
Zeichen, also wird es als jedermanns Lieblings-?
übersetzt . Aber warum stimmte das dann mit dem MD5-Hashwert in .NET überein, wenn die ASCII-Codierung verwendet wurde? Könnte es sein, dass wir den gehashten Wert von è
nicht wirklich abgeglichen haben , sondern stimmten stattdessen mit dem Hash-Wert von ?
überein :
SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
Jep. Der wahre ASCII-Zeichensatz ist nur die ersten 128 Zeichen (Werte 0 - 127). Und wie wir gerade gesehen haben, der è
ist 232. Verwenden Sie also den ASCII
Codierung in .NET ist nicht so hilfreich. COLLATE
wurde auch nicht verwendet auf der T-SQL-Seite.
Ist es möglich, auf der .NET-Seite eine bessere Codierung zu erhalten? Ja, mithilfe von Encoding.GetEncoding(Int32), wodurch die Codepage angegeben werden kann. Die zu verwendende Codepage kann mit der folgenden Abfrage ermittelt werden (verwenden Sie sys.columns
wenn mit einer Spalte statt mit einem Literal oder einer Variablen gearbeitet wird):
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
Die obige Abfrage gibt (für mich) zurück:
Latin1_General_100_CI_AS_SC 1252
Versuchen wir es also mit Codepage 1252:
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
Woo hoo! Wir haben eine Übereinstimmung für VARCHAR
Daten, die unsere standardmäßige SQL Server-Sortierung verwenden :). Wenn die Daten natürlich aus einer Datenbank oder einem Feld stammen, das auf eine andere Sortierung festgelegt ist, dann GetEncoding(1252)
vielleicht nicht funktionieren und Sie müssen die tatsächlich passende Codepage finden, indem Sie die oben gezeigte Abfrage verwenden (eine Codepage wird über viele Sortierungen hinweg verwendet, daher ist eine andere Sortierung nicht notwendigerweise erforderlich implizieren eine andere Codepage).
Um zu sehen, was die möglichen Codepage-Werte sind und zu welcher Kultur/Gebietsschema sie gehören, sehen Sie sich bitte die Liste der Codepages hier an (die Liste befindet sich im Abschnitt „Bemerkungen“).
Zusätzliche Informationen zu dem, was tatsächlich in NVARCHAR
gespeichert ist / NCHAR
Felder:
Jedes UTF-16-Zeichen (2 oder 4 Byte) kann gespeichert werden, obwohl das Standardverhalten der integrierten Funktionen davon ausgeht, dass alle Zeichen UCS-2 (jeweils 2 Byte) sind, was eine Teilmenge von UTF-16 ist. Ab SQL Server 2012 ist es möglich, auf eine Reihe von Windows-Sortierungen zuzugreifen, die die als ergänzende Zeichen bezeichneten 4-Byte-Zeichen unterstützen. Verwenden einer dieser Windows-Sortierungen, die auf _SC
enden , entweder für eine Spalte oder direkt in einer Abfrage angegeben, ermöglicht es den integrierten Funktionen, die 4-Byte-Zeichen richtig zu verarbeiten.
-- 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];
Rückgabe:
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
Wie Sie sehen können, weder DATALENGTH
noch HASHBYTES
sind betroffen. Weitere Informationen finden Sie auf der MSDN-Seite für Sortierung und Unicode-Unterstützung (insbesondere im Abschnitt „Ergänzende Zeichen“).