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 / 167489
Accepted
John G Hohengarten
John G Hohengarten
Asked: 2017-03-18 11:58:44 +0800 CST2017-03-18 11:58:44 +0800 CST 2017-03-18 11:58:44 +0800 CST

Detectar se algum valor nas colunas NVARCHAR é realmente unicode

  • 772

Eu herdei alguns bancos de dados SQL Server. Há uma tabela (chamarei "G"), com cerca de 86,7 milhões de linhas e 41 colunas de largura, de um banco de dados de origem (chamarei "Q") no SQL Server 2014 Standard que recebe ETL para um banco de dados de destino (chamarei "P") com o mesmo nome de tabela no SQL Server 2008 R2 Standard.

ou seja, [Q].[G] ---> [P].[G]

EDIT: 20/03/2017: Algumas pessoas perguntaram se a tabela de origem é a ÚNICA fonte da tabela de destino. Sim, é a única fonte. No que diz respeito ao ETL, não há nenhuma transformação real acontecendo; ele efetivamente se destina a ser uma cópia 1:1 dos dados de origem. Portanto, não há planos para adicionar fontes adicionais a esta tabela de destino.

Um pouco mais da metade das colunas em [Q].[G] são VARCHAR (tabela de origem):

  • 13 das colunas são VARCHAR(80)
  • 9 das colunas são VARCHAR(30)
  • 2 das colunas são VARCHAR(8).

Da mesma forma, as mesmas colunas em [P].[G] são NVARCHAR (target table), com o mesmo número de colunas com as mesmas larguras. (Em outras palavras, mesmo comprimento, mas NVARCHAR).

  • 13 das colunas são NVARCHAR(80)
  • 9 das colunas são NVARCHAR(30)
  • 2 das colunas são NVARCHAR(8).

Este não é o meu projeto.

Eu gostaria de ALTER [P].[G] (destino) os tipos de dados das colunas de NVARCHAR para VARCHAR. Eu quero fazer isso com segurança (sem perda de dados da conversão).

Como posso examinar os valores de dados em cada coluna NVARCHAR na tabela de destino para confirmar se a coluna realmente contém dados Unicode?

Uma consulta (DMVs?) que possa verificar cada valor (em um loop?) de cada coluna NVARCHAR e me diga se QUALQUER dos valores é genuíno Unicode seria a solução ideal, mas outros métodos são bem-vindos.

sql-server datatypes
  • 4 4 respostas
  • 18101 Views

4 respostas

  • Voted
  1. Best Answer
    Joe Obbish
    2017-03-18T12:48:14+08:002017-03-18T12:48:14+08:00

    Suponha que uma de suas colunas não contenha dados unicode. Para verificar se você precisaria ler o valor da coluna para cada linha. A menos que você tenha um índice na coluna, com uma tabela rowstore você precisará ler todas as páginas de dados da tabela. Com isso em mente, acho que faz muito sentido combinar todas as verificações de coluna em uma única consulta na tabela. Dessa forma, você não estará lendo os dados da tabela muitas vezes e não precisará codificar um cursor ou algum outro tipo de loop.

    Para verificar uma única coluna, acredite que você pode fazer isso:

    SELECT COLUMN_1
    FROM [P].[Q]
    WHERE CAST(COLUMN_1 AS VARCHAR(80)) <> CAST(COLUMN_1 AS NVARCHAR(80));
    

    Uma conversão de NVARCHARpara VARCHARdeve fornecer o mesmo resultado, exceto se houver caracteres unicode. Os caracteres Unicode serão convertidos para ?. Portanto, o código acima deve tratar NULLos casos corretamente. Você tem 24 colunas para verificar, então você verifica cada coluna em uma única consulta usando agregados escalares. Uma implementação está abaixo:

    SELECT 
      MAX(CASE WHEN CAST(COLUMN_1 AS VARCHAR(80)) <> CAST(COLUMN_1 AS NVARCHAR(80)) THEN 1 ELSE 0 END) COLUMN_1_RESULT
    ...
    , MAX(CASE WHEN CAST(COLUMN_14 AS VARCHAR(30)) <> CAST(COLUMN_14 AS NVARCHAR(30)) THEN 1 ELSE 0 END) COLUMN_14_RESULT
    ...
    , MAX(CASE WHEN CAST(COLUMN_23 AS VARCHAR(8)) <> CAST(COLUMN_23 AS NVARCHAR(8)) THEN 1 ELSE 0 END) COLUMN_23_RESULT
    FROM [P].[Q];
    

    Para cada coluna, você obterá um resultado 1se algum de seus valores contiver unicode. Um resultado 0significa que todos os dados podem ser convertidos com segurança.

    Eu recomendo fortemente fazer uma cópia da tabela com as novas definições de coluna e copiar seus dados lá. Você estará fazendo conversões caras se fizer isso no local, então fazer uma cópia pode não ser muito mais lento. Ter uma cópia significa que você pode validar facilmente que todos os dados ainda estão lá (uma maneira é usar a palavra-chave EXCEPT ) e você pode desfazer a operação com muita facilidade.

    Além disso, esteja ciente de que você pode não ter nenhum dado unicode no momento, é possível que um ETL futuro carregue unicode em uma coluna anteriormente limpa. Se não houver uma verificação para isso em seu processo de ETL, considere adicioná-la antes de fazer essa conversão.

    • 10
  2. Solomon Rutzky
    2017-03-18T19:32:43+08:002017-03-18T19:32:43+08:00

    Antes de fazer qualquer coisa, considere as perguntas feitas por @RDFozz em um comentário sobre a pergunta, a saber:

    1. Existem outras fontes além [Q].[G]de preencher esta tabela?

      Se a resposta for algo diferente de "Estou 100% certo de que esta é a única fonte de dados para esta tabela de destino", não faça nenhuma alteração, independentemente de os dados atualmente na tabela poderem ser convertidos sem perda de dados.

    2. Existem planos/discussões relacionadas à adição de fontes adicionais para preencher esses dados em um futuro próximo ?

      E eu acrescentaria uma pergunta relacionada: Houve alguma discussão sobre o suporte a vários idiomas na tabela de origem atual (ou seja [Q].[G], ) convertendo - a para NVARCHAR?

      Você precisará perguntar ao redor para ter uma noção dessas possibilidades. Eu suponho que você não tenha sido informado de nada que apontasse nessa direção, caso contrário você não estaria fazendo essa pergunta, mas se essas perguntas foram consideradas "não", elas precisam ser feitas e feitas de um público amplo o suficiente para obter a resposta mais precisa/completa.

    O principal problema aqui não é tanto ter pontos de código Unicode que não podem ser convertidos (nunca), mas sim ter pontos de código que não cabem em uma única página de código. Essa é a vantagem do Unicode: ele pode conter caracteres de TODAS as páginas de código. Se você converter de NVARCHAR– de onde não precisa se preocupar com páginas de código – para VARCHAR, precisará certificar-se de que o Collation da coluna de destino esteja usando a mesma página de código que a coluna de origem. Isso pressupõe ter uma fonte ou várias fontes usando a mesma página de código (não necessariamente o mesmo Collation). Mas se houver várias fontes com várias páginas de código, você poderá se deparar com o seguinte problema:

    DECLARE @Reporting TABLE
    (
      ID INT IDENTITY(1, 1) PRIMARY KEY,
      SourceSlovak VARCHAR(50) COLLATE Slovak_CI_AS,
      SourceHebrew VARCHAR(50) COLLATE Hebrew_CI_AS,
      Destination NVARCHAR(50) COLLATE Latin1_General_CI_AS,
      DestinationS VARCHAR(50) COLLATE Slovak_CI_AS,
      DestinationH VARCHAR(50) COLLATE Hebrew_CI_AS
    );
    
    INSERT INTO @Reporting ([SourceSlovak]) VALUES (0xDE20FA);
    INSERT INTO @Reporting ([SourceHebrew]) VALUES (0xE820FA);
    
    UPDATE @Reporting
    SET    [Destination] = [SourceSlovak]
    WHERE  [SourceSlovak] IS NOT NULL;
    
    UPDATE @Reporting
    SET    [Destination] = [SourceHebrew]
    WHERE  [SourceHebrew] IS NOT NULL;
    
    SELECT * FROM @Reporting;
    
    UPDATE @Reporting
    SET    [DestinationS] = [Destination],
           [DestinationH] = [Destination]
    
    SELECT * FROM @Reporting;
    

    Retorna (2º conjunto de resultados):

    ID    SourceSlovak    SourceHebrew    Destination    DestinationS    DestinationH
    1     Ţ ú             NULL            Ţ ú            Ţ ú             ? ?
    2     NULL            ט ת             ? ?            ט ת             ט ת
    

    Como você pode ver, todos esses caracteres podem ser convertidos em VARCHAR, mas não na mesma VARCHARcoluna.

    Use a seguinte consulta para determinar qual é a página de código para cada coluna de sua tabela de origem:

    SELECT OBJECT_NAME(sc.[object_id]) AS [TableName],
           COLLATIONPROPERTY(sc.[collation_name], 'CodePage') AS [CodePage],
           sc.*
    FROM   sys.columns sc
    WHERE  OBJECT_NAME(sc.[object_id]) = N'source_table_name';
    

    DITO ISSO....

    Você mencionou estar no SQL Server 2008 R2, MAS, você não disse qual edição. SE você estiver na Enterprise Edition, esqueça todas essas coisas de conversão (já que provavelmente está fazendo isso apenas para economizar espaço) e ative a compactação de dados:

    Implementação de compactação Unicode

    Se estiver usando o Standard Edition (e agora parece que você está ? ), há outra possibilidade loooonga: atualizar para o SQL Server 2016, pois o SP1 inclui a capacidade de todas as edições usarem compactação de dados (lembre-se, eu disse "long-shot " ?).

    Claro, agora que acabamos de esclarecer que existe apenas uma fonte para os dados, você não precisa se preocupar com nada, pois a fonte não pode conter nenhum caractere somente Unicode ou caracteres fora de seu código específico página. Nesse caso, a única coisa que você deve ter em mente é usar o mesmo Collation da coluna de origem, ou pelo menos um que esteja usando a mesma Code Page. Ou seja, se a coluna de origem estiver usando SQL_Latin1_General_CP1_CI_AS, você poderá usar Latin1_General_100_CI_ASno destino.

    Depois de saber qual Collation usar, você pode:

    • ALTER TABLE ... ALTER COLUMN ...ser VARCHAR(certifique-se de especificar a configuração NULL/ atual NOT NULL), o que requer um pouco de tempo e muito espaço de log de transações para 87 milhões de linhas, OU

    • Crie novas colunas "ColumnName_tmp" para cada uma e preencha lentamente UPDATEfazendo TOP (1000) ... WHERE new_column IS NULL. Depois que todas as linhas forem preenchidas (e validado que todas foram copiadas corretamente! você pode precisar de um gatilho para lidar com UPDATEs, se houver), em uma transação explícita, use sp_renamepara trocar os nomes das colunas "atuais" para " _Old" e depois as novas colunas "_tmp" para simplesmente remover o "_tmp" dos nomes. Em seguida, chame sp_reconfigurea tabela para invalidar qualquer plano em cache que faça referência à tabela e, se houver alguma View fazendo referência à tabela, você precisará chamar sp_refreshview(ou algo assim). Depois de validar o aplicativo e o ETL estiver funcionando corretamente com ele, você poderá descartar as colunas.

    • 5
  3. Erik Darling
    2017-03-18T14:29:37+08:002017-03-18T14:29:37+08:00

    Eu tenho alguma experiência com isso de volta quando eu tinha um trabalho real. Como na época eu queria preservar os dados de base, e também tinha que levar em conta os novos dados que poderiam ter caracteres que se perderiam no embaralhamento, optei por uma coluna computada não persistente.

    Aqui está um exemplo rápido usando uma cópia do banco de dados de superusuário do despejo de dados SO .

    Podemos ver logo de cara que existem DisplayNames com caracteres Unicode:

    Nozes

    Então, vamos adicionar uma coluna computada para descobrir quantas! A coluna DisplayName é NVARCHAR(40).

    USE SUPERUSER
    
    ALTER TABLE dbo.Users
    ADD DisplayNameStandard AS CONVERT(VARCHAR(40), DisplayName)
    
    SELECT COUNT_BIG(*)
    FROM dbo.Users AS u
    WHERE u.DisplayName <> u.DisplayNameStandard
    

    A contagem retorna ~ 3.000 linhas

    Nozes

    O plano de execução é um pouco chato, no entanto. A consulta termina rapidamente, mas esse conjunto de dados não é muito grande.

    Nozes

    Como as colunas computadas não precisam ser persistidas para adicionar um índice, podemos fazer um destes:

    CREATE UNIQUE NONCLUSTERED INDEX ix_helper
    ON dbo.Users(DisplayName, DisplayNameStandard, Id)
    

    O que nos dá um plano um pouco mais organizado:

    Nozes

    Entendo que essa não seja a resposta, pois envolve alterações de arquitetura, mas, considerando o tamanho dos dados, você provavelmente está procurando adicionar índices para lidar com consultas que se unem à tabela de qualquer maneira.

    Espero que isto ajude!

    • 4
  4. Scott Hodgin - Retired
    2017-03-18T12:09:15+08:002017-03-18T12:09:15+08:00

    Usando o exemplo em Como verificar se um campo contém dados unicode , você pode ler os dados em cada coluna e fazer a CASTverificação abaixo:

    --Test 1:
    DECLARE @text NVARCHAR(100)
    SET @text = N'This is non-Unicode text, in Unicode'
    IF CAST(@text AS VARCHAR(MAX)) <> @text
    PRINT 'Contains Unicode characters'
    ELSE
    PRINT 'No Unicode characters'
    GO
    
    --Test 2:
    DECLARE @text NVARCHAR(100)
    SET @text = N'This is Unicode (字) text, in Unicode'
    IF CAST(@text AS VARCHAR(MAX)) <> @text
    PRINT 'Contains Unicode characters'
    ELSE
    PRINT 'No Unicode characters'
    
    GO
    
    • 1

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • 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