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.
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:
Uma conversão de
NVARCHAR
paraVARCHAR
deve fornecer o mesmo resultado, exceto se houver caracteres unicode. Os caracteres Unicode serão convertidos para?
. Portanto, o código acima deve tratarNULL
os 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:Para cada coluna, você obterá um resultado
1
se algum de seus valores contiver unicode. Um resultado0
significa 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.
Antes de fazer qualquer coisa, considere as perguntas feitas por @RDFozz em um comentário sobre a pergunta, a saber:
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.
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 paraNVARCHAR
?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 – paraVARCHAR
, 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:Retorna (2º conjunto de resultados):
Como você pode ver, todos esses caracteres podem ser convertidos em
VARCHAR
, mas não na mesmaVARCHAR
coluna.Use a seguinte consulta para determinar qual é a página de código para cada coluna de sua tabela de origem:
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á usarLatin1_General_100_CI_AS
no destino.Depois de saber qual Collation usar, você pode:
ALTER TABLE ... ALTER COLUMN ...
serVARCHAR
(certifique-se de especificar a configuraçãoNULL
/ atualNOT NULL
), o que requer um pouco de tempo e muito espaço de log de transações para 87 milhões de linhas, OUCrie novas colunas "ColumnName_tmp" para cada uma e preencha lentamente
UPDATE
fazendoTOP (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, usesp_rename
para trocar os nomes das colunas "atuais" para " _Old" e depois as novas colunas "_tmp" para simplesmente remover o "_tmp" dos nomes. Em seguida, chamesp_reconfigure
a tabela para invalidar qualquer plano em cache que faça referência à tabela e, se houver alguma View fazendo referência à tabela, você precisará chamarsp_refreshview
(ou algo assim). Depois de validar o aplicativo e o ETL estiver funcionando corretamente com ele, você poderá descartar as colunas.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:
Então, vamos adicionar uma coluna computada para descobrir quantas! A coluna DisplayName é
NVARCHAR(40)
.A contagem retorna ~ 3.000 linhas
O plano de execução é um pouco chato, no entanto. A consulta termina rapidamente, mas esse conjunto de dados não é muito grande.
Como as colunas computadas não precisam ser persistidas para adicionar um índice, podemos fazer um destes:
O que nos dá um plano um pouco mais organizado:
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!
Usando o exemplo em Como verificar se um campo contém dados unicode , você pode ler os dados em cada coluna e fazer a
CAST
verificação abaixo: