Se você estiver lidando com
NVARCHAR
/ NCHAR
dados (que são armazenados como UTF-16 Little Endian ), então você usaria o Unicode
codificação, não BigEndianUnicode
. Em .NET, UTF-16 é chamado de Unicode
enquanto outras codificações Unicode são referidas por seus nomes reais:UTF7, UTF8 e UTF32. Portanto, Unicode
por si só é Little Endian
ao contrário de BigEndianUnicode
. ATUALIZAÇÃO: Por favor, veja a seção no final sobre UCS-2 e caracteres suplementares. No lado do banco de dados:
SELECT HASHBYTES('MD5', N'è') AS [HashBytesNVARCHAR]
-- FAC02CD988801F0495D35611223782CF
No lado .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
No entanto, esta questão pertence a
VARCHAR
/ CHAR
data, que é ASCII, então as coisas são um pouco mais complicadas. No lado do banco de dados:
SELECT HASHBYTES('MD5', 'è') AS [HashBytesVARCHAR]
-- 785D512BE4316D578E6650613B45E934
Já vemos o lado .NET acima. A partir desses valores de hash, deve haver duas perguntas:
- Por que não nenhum deles correspondem aos
HASHBYTES
valor? - Por que o artigo "sqlteam.com" vinculado na resposta de @Eric J. mostra que três deles (
ASCII
,UTF7
eUTF8
) todos correspondem aosHASHBYTES
valor?
Há uma resposta que cobre ambas as perguntas:Páginas de código. O teste feito no artigo "sqlteam" usou caracteres ASCII "seguros" que estão no intervalo de 0 a 127 (em termos do valor int/decimal) que não variam entre as páginas de código. Mas o intervalo de 128 a 255 -- onde encontramos o caractere "è" -- é o Estendido set que varia de acordo com a página de código (o que faz sentido, pois esse é o motivo de ter páginas de código).
Agora tente:
SELECT HASHBYTES('MD5', 'è' COLLATE SQL_Latin1_General_CP1255_CI_AS) AS [HashBytes]
-- D1457B72C3FB323A2671125AEF3EAB5D
Isso corresponde ao
ASCII
valor com hash (e novamente, porque o artigo / teste "sqlteam" usou valores no intervalo de 0 a 127, eles não viram nenhuma alteração ao usar COLLATE
). Ótimo, agora finalmente encontramos uma maneira de combinar VARCHAR
/ CHAR
dados. Tudo certo? Bem, na verdade não. Vamos dar uma olhada no que estávamos realmente fazendo hash:
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];
Devoluções:
TheChar TheASCIIvalue CharCP1255 TheASCIIvalueCP1255
è 232 ? 63
Um
?
? Apenas para verificar, execute:SELECT CHAR(63) AS [WhatIs63?];
-- ?
Ah, então a página de código 1255 não tem o
è
personagem, então ele é traduzido como o ?
. Mas então por que isso corresponde ao valor de hash MD5 no .NET ao usar a codificação ASCII? Será que não estávamos realmente correspondendo ao valor de hash de è
, mas estavam correspondendo ao valor com hash de ?
:SELECT HASHBYTES('MD5', '?') AS [HashBytesVARCHAR]
-- 0xD1457B72C3FB323A2671125AEF3EAB5D
Sim. O verdadeiro conjunto de caracteres ASCII é apenas os primeiros 128 caracteres (valores 0 - 127). E como acabamos de ver, o
è
é 232. Então, usando o ASCII
codificação em .NET não é tão útil. Nem estava usando COLLATE
no lado T-SQL. É possível obter uma codificação melhor no lado .NET? Sim, usando Encoding.GetEncoding(Int32), que permite especificar a página de código. A página de código a ser usada pode ser descoberta usando a seguinte consulta (use
sys.columns
ao trabalhar com uma coluna em vez de um literal ou variável):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
A consulta acima retorna (para mim):
Latin1_General_100_CI_AS_SC 1252
Então, vamos tentar a página de código 1252:
System.Text.Encoding.GetEncoding(1252).GetBytes("è") // Matches HASHBYTES('MD5', 'è')
// 785D512BE4316D578E6650613B45E934
Uau! Temos uma correspondência para
VARCHAR
data que usa nosso agrupamento padrão do SQL Server :). Obviamente, se os dados vierem de um banco de dados ou conjunto de campos para um agrupamento diferente, GetEncoding(1252)
pode não funcionar e você terá que encontrar a página de código correspondente usando a consulta mostrada acima (uma página de código é usada em muitos agrupamentos, portanto, um agrupamento diferente não necessariamente implicar uma página de código diferente). Para ver quais são os possíveis valores de página de código e a que cultura/localidade eles pertencem, consulte a lista de páginas de código aqui (a lista está na seção "Observações").
Informações adicionais relacionadas ao que está realmente armazenado em
NVARCHAR
/ NCHAR
campos: Qualquer caractere UTF-16 (2 ou 4 bytes) pode ser armazenado, embora o comportamento padrão das funções internas suponha que todos os caracteres sejam UCS-2 (2 bytes cada), que é um subconjunto de UTF-16. A partir do SQL Server 2012, é possível acessar um conjunto de agrupamentos do Windows que dão suporte aos caracteres de 4 bytes conhecidos como caracteres suplementares. Usando um desses agrupamentos do Windows que terminam em
_SC
, especificado para uma coluna ou diretamente em uma consulta, permitirá que as funções internas manipulem adequadamente os caracteres de 4 bytes. -- 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];
Devoluções:
SupplementaryChar LEN DATALENGTH UNICODE LEFT HASHBYTES
𨝫 2 4 55393 � 0x7A04F43DA81E3150F539C6B99F4B8FA9
𨝫 1 4 165739 𨝫 0x7A04F43DA81E3150F539C6B99F4B8FA9
Como você pode ver, nem
DATALENGTH
nem HASHBYTES
são afetados. Para obter mais informações, consulte a página do MSDN para suporte a agrupamento e Unicode (especificamente a seção "Caracteres suplementares").