Als u te maken heeft met NVARCHAR
/ NCHAR
gegevens (die worden opgeslagen als UTF-16 Little Endian ), dan gebruikt u de Unicode
codering, niet BigEndianUnicode
. In .NET heet UTF-16 Unicode
terwijl andere Unicode-coderingen worden aangeduid met hun werkelijke naam:UTF7, UTF8 en UTF32. Vandaar, Unicode
op zichzelf is Little Endian
in tegenstelling tot BigEndianUnicode
. UPDATE: Zie het gedeelte aan het einde over UCS-2 en aanvullende tekens.
Aan de databasekant:
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
Aan de .NET-kant:
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
Deze vraag heeft echter betrekking op VARCHAR
/ CHAR
data, wat ASCII is, en dus zijn de dingen een beetje ingewikkelder.
Aan de databasekant:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
We zien hierboven al de .NET-kant. Van die gehashte waarden moeten er twee vragen zijn:
- Waarom geen van hen komen overeen met de
HASHBYTES
waarde? - Waarom blijkt uit het artikel "sqlteam.com" dat is gelinkt in het antwoord van @Eric J. dat drie van hen (
ASCII
,UTF7
, enUTF8
) komen allemaal overeen met deHASHBYTES
waarde?
Er is één antwoord dat beide vragen dekt:codepagina's. De test in het "sqlteam"-artikel gebruikte "veilige" ASCII-tekens die in het bereik van 0 - 127 liggen (in termen van de int / decimale waarde) die niet variëren tussen codepagina's. Maar het bereik van 128 - 255 -- waar we het teken "è" vinden -- is de Uitgebreide set die wel verschilt per codepagina (wat logisch is omdat dit de reden is voor het hebben van codepagina's).
Probeer nu:
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
Dat komt overeen met de ASCII
gehashte waarde (en nogmaals, omdat het "sqlteam" artikel / test waarden in het bereik van 0 - 127 gebruikte, zagen ze geen veranderingen bij het gebruik van COLLATE
). Geweldig, nu hebben we eindelijk een manier gevonden om VARCHAR
te matchen / CHAR
gegevens. Alles goed?
Nou niet echt. Laten we eens kijken naar wat we eigenlijk aan het hashen waren:
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];
Retourneren:
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
Een ?
? Voer ter verificatie het volgende uit:
SELECT CHAR(63) AS [WhatIs63?];
-- ?
Ah, dus codepagina 1255 heeft niet de è
karakter, dus het wordt vertaald als ieders favoriete ?
. Maar waarom kwam dat overeen met de MD5-gehashte waarde in .NET bij gebruik van de ASCII-codering? Zou het kunnen dat we niet echt overeenkomen met de gehashte waarde van è
, maar kwamen in plaats daarvan overeen met de gehashte waarde van ?
:
SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
JEP. De echte ASCII-tekenset is slechts de eerste 128 tekens (waarden 0 - 127). En zoals we net zagen, de è
is 232. Dus, met behulp van de ASCII
codering in .NET is niet zo handig. Evenmin gebruikte COLLATE
aan de T-SQL-kant.
Is het mogelijk om een betere codering te krijgen aan de .NET-kant? Ja, door Encoding.GetEncoding(Int32) te gebruiken, waarmee de codepagina kan worden gespecificeerd. De te gebruiken codepagina kan worden gevonden met behulp van de volgende zoekopdracht (gebruik sys.columns
bij het werken met een kolom in plaats van een letterlijke of variabele):
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
De bovenstaande vraag retourneert (voor mij):
Latin1_General_100_CI_AS_SC 1252
Laten we dus codepagina 1252 proberen:
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
Woehoe! We hebben een match voor VARCHAR
gegevens die onze standaard SQL Server-sortering gebruiken :). Als de gegevens afkomstig zijn uit een database of een veld dat is ingesteld op een andere sortering, dan is GetEncoding(1252)
natuurlijk misschien werkt niet en u zult de daadwerkelijk overeenkomende codepagina moeten vinden met behulp van de bovenstaande zoekopdracht (een codepagina wordt voor veel sorteringen gebruikt, dus een andere sortering is niet noodzakelijk impliceert een andere codepagina).
Om te zien wat de mogelijke codepagina-waarden zijn, en tot welke cultuur / locale ze behoren, raadpleegt u de lijst met codepagina's hier (de lijst staat in het gedeelte "Opmerkingen").
Aanvullende informatie met betrekking tot wat er feitelijk is opgeslagen in NVARCHAR
/ NCHAR
velden:
Elk UTF-16-teken (2 of 4 bytes) kan worden opgeslagen, hoewel het standaardgedrag van de ingebouwde functies ervan uitgaat dat alle tekens UCS-2 zijn (elk 2 bytes), wat een subset is van UTF-16. Vanaf SQL Server 2012 is het mogelijk om toegang te krijgen tot een set Windows-sorteringen die de 4-byte-tekens ondersteunen die bekend staan als aanvullende tekens. Een van deze Windows-sorteringen gebruiken die eindigen op _SC
, gespecificeerd voor een kolom of rechtstreeks in een query, zorgen ervoor dat de ingebouwde functies de 4-byte-tekens correct kunnen verwerken.
-- 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];
Retourneren:
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
Zoals je kunt zien, is geen van beide DATALENGTH
noch HASHBYTES
zijn aangetast. Raadpleeg voor meer informatie de MSDN-pagina voor ondersteuning voor sorteren en Unicode (met name de sectie "Aanvullende tekens").