Tradução automática ao converter Unicode para não-Unicode / NVARCHAR para VARCHAR

8

O ponto de código Unicode 9619 é um caractere chamado "Tonalidade escura": ( http://unicode-table.com/en/search/?q=9619 ).

Usando o SQL_Latin1_General_CP1_CI_ASagrupamento e a página de código 1252, eu esperaria que a conversão / conversão desse caractere Unicode para o tipo de dados não Unicode resultasse em um ponto de interrogação ( ?), pois a página de código 1252 não parece conter esse caractere e isso parece ser do SQL Server comportamento quando a conversão não pode ocorrer.

Então, minha pergunta é: por que o SQL Server converte esse caractere em um código ASCII 166 que é "Pipe, Broken vertical bar" ¦:?

SELECT NCHAR(9619), CAST(NCHAR(9619) AS CHAR(1)), ASCII(CAST(NCHAR(9619) AS CHAR(1)))
Henry Lee
fonte
3
O SQL Server usa o que este documento chama de transformação homoglífica e muitas vezes converte caracteres que não podem ser representados em equivalentes próximos. Como perder o sotaque de um personagem ou alterar aspas inteligentes para aspas simples. Concordo que não parece muito próximo! Não tenho certeza se ou onde essas transformações estão documentadas.
Martin Smith
Uau, não fazia ideia ... caramba, simplesmente não parece certo ... não é o mesmo personagem. Por que não apenas um "... opa, nenhum caractere encontrado nesta página de códigos ..." e falha na conversão?
Henry Lee
11
Basta ler esta página e lembrei-me disso. Não tenho certeza se o SQL Server usa exatamente os mesmos algoritmos de "melhor ajuste".
Martin Smith
11
@ MartinSmith a respeito de não ter certeza dos mapeamentos "mais adequados" para o SQL Server, veja a minha resposta abaixo quando encontrei esses mapeamentos :-).
Solomon Rutzky

Respostas:

8

Por que SQL converte Unicode 9619 em código ASCII 166?

O SQL Server não está empregando nenhuma lógica personalizada especial aqui; está usando serviços padrão do sistema operacional para executar a conversão.

Especificamente, o serviço de tipo e expressão do SQL Server ( sqlTsEs) chama a rotina do SO WideCharToMultiByteem kernel32.dll. O SQL Server define os parâmetros de entrada para WideCharToMultiByteque a rotina execute uma 'tradução rápida'. Isso é mais rápido do que solicitar que um caractere padrão específico seja usado quando não houver tradução direta.

A tradução rápida se baseia na página de código de destino para executar um mapeamento mais adequado para caracteres sem correspondência, conforme mencionado no link que Martin Smith forneceu em um comentário à pergunta:

As estratégias de melhor ajuste variam para diferentes páginas de código e não são documentadas em detalhes.

Quando os parâmetros de entrada são definidos para uma tradução rápida, WideCharToMultiBytechama o serviço do SO GetMBNoDefault( origem ). A inspeção da pilha de chamadas do SQL Server ao executar a conversão especificada na pergunta confirma isso:

Rastreio de pilha do SQL Server

Paul White 9
fonte
7

A conversão de dados Unicode em uma página de código específica emprega a estratégia conhecida como "Melhor ajuste" (conforme observado na resposta de @ Paul e no link que @Martin anotou em um comentário sobre a Pergunta). De acordo com a página do MSDN para codificação de caracteres no .NET Framework :

O mapeamento de melhor ajuste é o comportamento padrão de um objeto Encoding que codifica dados Unicode em dados da página de código ...

Mas o que exatamente são esses mapeamentos? Essa página do MSDN costumava indicar o seguinte:

As estratégias de melhor ajuste variam para diferentes páginas de código e não são documentadas em detalhes.

No entanto, isso não estava totalmente correto. Talvez as "estratégias" para determinar os mapeamentos não estejam exatamente documentadas. Está bem. Mas, os próprios mapeamentos são documentados, mas não nos lugares mais fáceis de encontrar.

Portanto, graças à Microsoft movendo a documentação para o GitHub, essa página agora declara o seguinte (porque eu a atualizei):

As estratégias de melhor ajuste não são documentadas em detalhes. No entanto, várias páginas de código estão documentadas no site do Unicode Consortium . Revise o arquivo readme.txt nessa pasta para obter uma descrição de como interpretar os arquivos de mapeamento.

Se você for para o URL a seguir, verá uma lista de vários arquivos, cada um nomeado para a Página de Código para a qual mapeia caracteres Unicode:

ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/

A maioria dos arquivos foi atualizada pela última vez (ou pelo menos foi colocada lá) em 04/10/2006, e um deles foi atualizado em 14/03/2012. A primeira parte desses arquivos mapeia códigos ASCII em um ponto de código Unicode equivalente. Mas a segunda parte de cada arquivo mapeia os caracteres Unicode em seus "equivalentes" ASCII.

Eu escrevi um script de teste que usa os mapeamentos de código para verificar se o SQL Server está realmente usando esses mapeamentos. Isso pode ser determinado respondendo a essas duas perguntas:

  1. Para todos os Code Points mapeados, o SQL Server os converte nos mapeamentos especificados?
  2. Para todos os Code Points não mapeados, o SQL Server converte algum deles em um ?caractere " " não " "?

O script de teste é muito longo para ser colocado aqui, então eu o publiquei no Pastebin em:

Mapeamentos Unicode para Página de Código no SQL Server

A execução do script mostrará que a resposta para a primeira pergunta acima é "Sim" (significando que todos os mapeamentos fornecidos são respeitados). Também mostrará que a resposta para a segunda pergunta é "Não" (ou seja, nenhum dos Pontos de código não mapeados se converte em nada além do caractere "desconhecido"). Portanto, esse arquivo de mapeamento é muito preciso :-).

Solomon Rutzky
fonte