Eu herdei alguns bancos de dados do 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 obtém o 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 à ETL, não há nenhuma transformação real acontecendo; efetivamente pretende 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 (tabela de destino), 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 design.
Gostaria de alterar os tipos de dados das colunas [P]. [G] (destino) de NVARCHAR para VARCHAR. Quero fazê-lo com segurança (sem perda de dados da conversão).
Como posso observar os valores dos dados em cada coluna NVARCHAR na tabela de destino para confirmar se a coluna realmente contém ou não dados Unicode?
Uma consulta (DMVs?) Que pode verificar cada valor (em um loop?) De cada coluna NVARCHAR e me informar se QUALQUER valor é genuíno, o Unicode seria a solução ideal, mas outros métodos são bem-vindos.
fonte
[G]
são ETLed para[P]
. Se[G]
forvarchar
, e o processo ETL é a única maneira pela qual os dados entram[P]
, a menos que o processo adicione caracteres Unicode verdadeiros, não deve haver nenhum. Se outros processos adicionam ou modificam dados[P]
, você precisa ter mais cuidado - apenas porque todos os dados atuais podem estarvarchar
, não significa que osnvarchar
dados não possam ser adicionados amanhã. Da mesma forma, é possível que o que estiver consumindo os dados[P]
precise denvarchar
dados.Respostas:
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. Caracteres Unicode serão convertidos para?
. Portanto, o código acima deve tratar osNULL
casos corretamente. Você tem 24 colunas para verificar, portanto, verifique 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 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, para fazer uma cópia não ser muito mais lenta. Ter uma cópia significa que você pode facilmente validar 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 dados unicode atualmente. É possível que um ETL futuro possa carregar o unicode em uma coluna limpa anteriormente. Se não houver uma verificação para isso em seu processo ETL, considere adicionar isso antes de fazer essa conversão.
fonte
NVARCHAR
coluna,NVARCHAR
pois ela já é desse tipo. E não sabe como você determinou o caractere inconversível, mas é possível converter a colunaVARBINARY
para obter as seqüências de bytes UTF-16. E UTF-16 é a ordem inversa de bytes, entãop
=0x7000
e então você inverte esses dois bytes para obter o Ponto de CódigoU+0070
. Mas, se a fonte for VARCHAR, não poderá ser um caractere Unicode. Algo mais está acontecendo. Precisa de mais informações.VARCHAR
será implicitamente convertido emNVARCHAR
, mas pode ser melhorCONVERT(NVARCHAR(80), CONVERT(VARCHAR(80), column)) <> column
.SUBSTRING
às vezes funciona, mas não funciona com caracteres suplementares ao usar agrupamentos que não terminam_SC
e o que John está usando não, embora provavelmente não seja um problema aqui. Mas converter para VARBINARY sempre funciona. ECONVERT(VARCHAR(10), CONVERT(NVARCHAR(10), '›'))
não resulta?
, então eu gostaria de ver os bytes. O processo ETL pode ter convertido.Antes de fazer qualquer coisa, considere as perguntas feitas pelo @RDFozz em um comentário sobre a pergunta, a saber:
Se a resposta estiver fora de "Estou 100% certo de que esta é a única fonte de dados para esta tabela de destino", não faça alterações, independentemente de os dados atualmente na tabela poderem ser convertidos ou não 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 (ie
[Q].[G]
) convertendo -a paraNVARCHAR
?Você precisará perguntar ao redor para ter uma idéia dessas possibilidades. Presumo que você não tenha recebido nenhuma informação que apontaria nessa direção, caso contrário, você não faria essa pergunta, mas se essas perguntas forem consideradas "não", elas deverão ser feitas e solicitadas a 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 ainda mais ter pontos de código que nem todos se encaixam em uma única página de código. Essa é a coisa legal do Unicode: ele pode conter caracteres de TODAS as páginas de código. Se você converter de
NVARCHAR
- para onde não precisa se preocupar com páginas de código - paraVARCHAR
, precisará garantir que o agrupamento 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 (embora não necessariamente o mesmo agrupamento). Mas se houver várias fontes com várias páginas de código, você poderá executar o seguinte problema:Retorna (segundo 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 da sua tabela de origem:
QUE ESTÁ DISSE ....
Você mencionou estar no SQL Server 2008 R2, MAS, não disse qual edição. Se você estiver no Enterprise Edition, esqueça todas essas conversões (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 você estiver usando o Standard Edition (e agora parece que você é 😞), há outra possibilidade de longo prazo: atualizar para o SQL Server 2016, já que o SP1 inclui a capacidade de todas as edições usarem a compactação de dados (lembre-se, eu disse "longo prazo "😉).
Obviamente, agora que acabou de ser esclarecido que há apenas uma fonte para os dados, você não precisa se preocupar, pois a fonte não pode conter caracteres somente Unicode ou caracteres fora do código específico. página. Nesse caso, a única coisa que você deve ter em mente é usar o mesmo agrupamento da coluna de origem ou pelo menos um que esteja usando a mesma página de código. 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 agrupamento usar, você pode:
ALTER TABLE ... ALTER COLUMN ...
para serVARCHAR
(não se esqueça de especificar a correnteNULL
/NOT NULL
configuração), que requer um pouco de tempo e um monte de espaço de log de transação 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 validadas, todas copiadas corretamente! Você pode precisar de um gatilho para manipular UPDATEs, se houver), em uma transação explícita, usesp_rename
para trocar os nomes das colunas "atuais" a serem " _Old "e, em seguida, as novas colunas" _tmp "para simplesmente remover o" _tmp "dos nomes. Em seguida, chamesp_reconfigure
a tabela para invalidar quaisquer planos em cache que façam referência à tabela e, se houver Visualizações que façam referência à tabela, você precisará chamarsp_refreshview
(ou algo assim). Depois de validar o aplicativo e o ETL estiver funcionando corretamente, você poderá soltar as colunas.fonte
Latin1_General_100_CI_AS
seja muito melhor que o que você está usando. Fácil significa que o comportamento de classificação e comparação será o mesmo entre eles, mesmo que não seja tão bom quanto o Collation mais recente que acabei de mencionar.Tenho alguma experiência nisso quando tinha um emprego de verdade. Como na época eu queria preservar os dados de base, e também tinha que dar conta de novos dados que poderiam ter caracteres que seriam perdidos no shuffle, fui com uma coluna computada não persistente.
Aqui está um exemplo rápido usando uma cópia do banco de dados Superusuário do despejo de dados do 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 ~ 3000 linhas
O plano de execução é um pouco complicado, 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 é a resposta, pois envolve alterações arquiteturais, mas considerando o tamanho dos dados, você provavelmente está adicionando índices para lidar com consultas que se auto-associam à tabela de qualquer maneira.
Espero que isto ajude!
fonte
Usando o exemplo em Como verificar se um campo contém dados unicode , você pode ler os dados em cada coluna e fazer
CAST
oe verifique abaixo:fonte