AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 229103
Accepted
HandyD
HandyD
Asked: 2019-02-07 19:12:28 +0800 CST2019-02-07 19:12:28 +0800 CST 2019-02-07 19:12:28 +0800 CST

Ordenação de bytes para caracteres multibyte no SQL Server versus Oracle

  • 772

Atualmente, estou no processo de migração de dados do Oracle para o SQL Server e estou encontrando um problema ao tentar validar os dados pós-migração.

Detalhes do ambiente:

  • Oracle 12 - conjunto de caracteres AL32UTF8
  • Cliente - NLS_LANG - WE8MSWIN1252
  • Campo VARCHAR2

SQL Server 2016

  • Agrupamento Latin1_General_CI_AS
  • Campo NVARCHAR

Estou usando DBMS_CRYPTO.HASH no Oracle para gerar uma soma de verificação de toda a linha, copiando para SQL e usando HASHBYTES para gerar uma soma de verificação de toda a linha, que estou comparando para validar as correspondências de dados.

As somas de verificação correspondem a todas as linhas, exceto aquelas com caracteres multibyte.

Por exemplo, linhas com este caractere: ◦ não correspondem nas somas de verificação, mesmo que os dados sejam transferidos corretamente. Quando uso DUMP no Oracle ou converto para VARBINARY no SQL Server, os dados correspondem exatamente, exceto os bytes desse caractere.

No SQL Server, os bytes são 0xE625 e no Oracle são 0x25E6.

Por que eles são ordenados de forma diferente e existe uma maneira confiável de converter um para o outro para garantir que a soma de verificação na outra extremidade corresponda a strings com caracteres de vários bytes?

sql-server oracle
  • 2 2 respostas
  • 1601 Views

2 respostas

  • Voted
  1. Best Answer
    Solomon Rutzky
    2019-02-08T11:02:05+08:002019-02-08T11:02:05+08:00

    O agrupamento de uma coluna NVARCHAR/ NCHAR/ NTEXTnão tem relação com a codificação usada para armazenar os dados nessa coluna. NVARCHARos dados são sempre UTF-16 Little Endian (LE). O agrupamento de NVARCHARdados afeta apenas a classificação e a comparação. O agrupamento afeta a codificação dos VARCHARdados, pois o agrupamento determina a página de código usada para armazenar os dados nessa coluna/variável/literal, mas não estamos lidando com isso aqui.

    Como sepupic mencionou , o que você está vendo quando visualiza os dados em formato binário é uma diferença de endianness (Oracle está usando Big Endian enquanto o SQL Server está usando Little Endian). NO ENTANTO, o que você está vendo quando visualiza a forma binária da string no Oracle não é como os dados estão realmente sendo armazenados. Você está usando AL32UTF8o que é UTF-8, que codifica esse caractere em 3 bytes, não em 2, como: E2, 97, A6.

    Além disso, não é possível que os hashes sejam os mesmos para linhas de apenas "a", mas não quando incluem "◦", a menos que o hash no Oracle tenha sido feito sem conversão, portanto, usando a codificação UTF-8 e o hash no SQL Server convertendo acidentalmente para VARCHARprimeiro. Caso contrário, não há algoritmo de hash que se comporte como você está descrevendo, como você pode verificar executando o seguinte no SQL Server:

    DECLARE @Algorithm NVARCHAR(50) = N'MD4';
    SELECT HASHBYTES(@Algorithm, 0x3100), HASHBYTES(@Algorithm, 0x0031);
    SET @Algorithm = N'MD5';
    SELECT HASHBYTES(@Algorithm, 0x3100), HASHBYTES(@Algorithm, 0x0031);
    SET @Algorithm = N'SHA1';
    SELECT HASHBYTES(@Algorithm, 0x3100), HASHBYTES(@Algorithm, 0x0031);
    SET @Algorithm = N'SHA2_256';
    SELECT HASHBYTES(@Algorithm, 0x3100), HASHBYTES(@Algorithm, 0x0031);
    SET @Algorithm = N'SHA2_512';
    SELECT HASHBYTES(@Algorithm, 0x3100), HASHBYTES(@Algorithm, 0x0031);
    

    No Oracle, você deve usar a CONVERTfunção para obter a string na AL16UTF16LEcodificação e, em seguida, fazer o hash desse valor. Isso deve corresponder ao que o SQL Server tem. Por exemplo, você pode ver as diferentes formas de codificação do White Bullet (U + 25E6) e como usar CONVERTjunto com AL16UTF16LEcorrigirá isso no dbfiddle e abaixo:

    SELECT DUMP(CHR(14849958), 1016) AS "UTF8",
           DUMP(CHR(9702 USING NCHAR_CS), 1016) AS "UTF16BE",
           DUMP(CONVERT(CHR(9702 USING NCHAR_CS), 'AL16UTF16LE' ), 1016) AS "UTF16LE"
    FROM DUAL;
    
    SELECT DUMP('a' || CHR(14849958), 1016) AS "UTF8",
           DUMP('a' || CHR(9702 USING NCHAR_CS), 1016) AS "UTF16BE",
           DUMP(CONVERT('a' || CHR(9702 USING NCHAR_CS), 'AL16UTF16LE' ), 1016) AS "UTF16LE"
    FROM DUAL;
    

    Que retorna:

    UTF8:     Typ=1 Len=3 CharacterSet=AL32UTF8: e2,97,a6
    UTF16BE:  Typ=1 Len=2 CharacterSet=AL16UTF16: 25,e6
    UTF16LE:  Typ=1 Len=2 CharacterSet=AL16UTF16: e6,25
    
    
    UTF8:     Typ=1 Len=4 CharacterSet=AL32UTF8: 61,e2,97,a6
    UTF16BE:  Typ=1 Len=4 CharacterSet=AL16UTF16: 0,61,25,e6
    UTF16LE:  Typ=1 Len=4 CharacterSet=AL16UTF16: 61,0,e6,25
    

    Como você pode ver na 3ª coluna, o conjunto de caracteres é relatado erroneamente como sendo Big Endian quando é claramente Little Endian com base na ordem dos dois bytes. Você também pode ver que ambos os caracteres são dois bytes em UTF-16, e a ordem de ambos é diferente entre Big e Little Endian, não apenas os caracteres que são > 1 byte em UTF-8.

    Dado tudo isso, como os dados estão sendo armazenados como UTF-8, mas você os está vendo como UTF-16 Big Endian por meio da DUMPfunção, parece que você já está convertendo para UTF-16, mas provavelmente não percebendo que o padrão UTF-16 no Oracle é Big Endian.

    Observando a definição "UTF-16" na página Glossário da documentação do Oracle , ela afirma (dividi as seguintes frases em duas partes para que seja mais fácil distinguir entre BE e LE):

    AL16UTF16 implementa o esquema de codificação big-endian da forma de codificação UTF-16 (o byte mais significativo de cada unidade de código vem primeiro na memória). AL16UTF16 é um conjunto de caracteres nacional válido.

    e:

    AL16UTF16LE implementa o esquema de codificação UTF-16 little-endian. É um conjunto de caracteres somente de conversão, válido apenas em funções de conversão de conjunto de caracteres, como SQL CONVERTou PL/SQL UTL_I18N.STRING_TO_RAW.

    PS Como você está usando AL32UTF8no Oracle, você deve usar o Latin1_General_100_CI_AS_SCagrupamento no SQL Server, não no Latin1_General_CI_AS. O que você está usando é mais antigo e não suporta totalmente caracteres suplementares (sem perda de dados se existirem, mas as funções internas os tratam como 2 caracteres em vez de uma única entidade).

    • 5
  2. sepupic
    2019-02-08T07:22:55+08:002019-02-08T07:22:55+08:00

    O que você está vendo é uma Little-Endiancodificação que SQL Serverusa para armazenar Unicodecaracteres (mais precisamente, usa UCS-2 LE).

    Mais Little-Endianaqui: Diferença entre a ordem Big Endian e Little Endian Byte

    Eu não sei como foi possível isso

    Quando uso DUMP no Oracle ou converto para VARBINARY no SQL Server os dados correspondem exatamente, exceto os bytes desse caractere

    Todos os Unicodecaracteres armazenados em SQL Server, convertidos em binary, são "invertidos", quer dizer, para ver os códigos reais você deve dividi-los em grupos de 2 bytese inverter a ordem dentro de cada par.

    Exemplo:

    declare @str varchar(3) = 'abc';
    declare @str_n nvarchar(3) = N'abc';
    
    select cast(@str as varbinary(3));
    select cast(@str_n as varbinary(6));
    

    O resultado é

    0x616263
    
    0x610062006300
    

    Como você vê no caso de Unicodebytes de caracteres serem invertidos: "a" é representado como 0x6100e não como 0x0061.

    A mesma história é sobre o código 0x25E6real enquanto em representação você o vê como , ou seja .UnicodebinarySQL Server0xE625inverted

    • 4

relate perguntas

  • ORDER BY usando prioridades personalizadas para colunas de texto

  • Interface sqlplus confortável? [fechado]

  • Como encontrar as instruções SQL mais recentes no banco de dados?

  • Como posso consultar nomes usando expressões regulares?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve