Estamos fazendo um processo ETL. Quando tudo estiver dito e feito, há várias tabelas que devem ser idênticas. Qual é a maneira mais rápida de verificar se essas tabelas (em dois servidores diferentes) são de fato idênticas. Estou falando de esquema e dados.
Posso fazer um hash na tabela que eu próprio faria em um arquivo ou grupo de arquivos individual - para comparar um com o outro. Temos dados do Red-Gate comparados, mas como as tabelas em questão contêm milhões de linhas cada, eu gostaria de algo um pouco mais de desempenho.
Uma abordagem que me intriga é esse uso criativo da declaração sindical . Mas, gostaria de explorar um pouco mais a idéia de hash, se possível.
ATUALIZAÇÃO DE RESPOSTA PÓS
Para futuros visitantes ... aqui está a abordagem exata que acabei adotando. Funcionou tão bem que estamos fazendo isso em todas as tabelas em cada banco de dados. Obrigado às respostas abaixo por me apontarem na direção certa.
CREATE PROCEDURE [dbo].[usp_DatabaseValidation]
@TableName varchar(50)
AS
BEGIN
SET NOCOUNT ON;
-- parameter = if no table name was passed do them all, otherwise just check the one
-- create a temp table that lists all tables in target database
CREATE TABLE #ChkSumTargetTables ([fullname] varchar(250), [name] varchar(50), chksum int);
INSERT INTO #ChkSumTargetTables ([fullname], [name], [chksum])
SELECT DISTINCT
'[MyDatabase].[' + S.name + '].['
+ T.name + ']' AS [fullname],
T.name AS [name],
0 AS [chksum]
FROM MyDatabase.sys.tables T
INNER JOIN MyDatabase.sys.schemas S ON T.schema_id = S.schema_id
WHERE
T.name like IsNull(@TableName,'%');
-- create a temp table that lists all tables in source database
CREATE TABLE #ChkSumSourceTables ([fullname] varchar(250), [name] varchar(50), chksum int)
INSERT INTO #ChkSumSourceTables ([fullname], [name], [chksum])
SELECT DISTINCT
'[MyLinkedServer].[MyDatabase].[' + S.name + '].['
+ T.name + ']' AS [fullname],
T.name AS [name],
0 AS [chksum]
FROM [MyLinkedServer].[MyDatabase].sys.tables T
INNER JOIN [MyLinkedServer].[MyDatabase].sys.schemas S ON
T.schema_id = S.schema_id
WHERE
T.name like IsNull(@TableName,'%');;
-- build a dynamic sql statement to populate temp tables with the checksums of each table
DECLARE @TargetStmt VARCHAR(MAX)
SELECT @TargetStmt = COALESCE(@TargetStmt + ';', '')
+ 'UPDATE #ChkSumTargetTables SET [chksum] = (SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM '
+ T.FullName + ') WHERE [name] = ''' + T.Name + ''''
FROM #ChkSumTargetTables T
SELECT @TargetStmt
DECLARE @SourceStmt VARCHAR(MAX)
SELECT @SourceStmt = COALESCE(@SourceStmt + ';', '')
+ 'UPDATE #ChkSumSourceTables SET [chksum] = (SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM '
+ S.FullName + ') WHERE [name] = ''' + S.Name + ''''
FROM #ChkSumSourceTables S
-- execute dynamic statements - populate temp tables with checksums
EXEC (@TargetStmt);
EXEC (@SourceStmt);
--compare the two databases to find any checksums that are different
SELECT TT.FullName AS [TABLES WHOSE CHECKSUM DOES NOT MATCH]
FROM #ChkSumTargetTables TT
LEFT JOIN #ChkSumSourceTables ST ON TT.Name = ST.Name
WHERE IsNull(ST.chksum,0) <> IsNull(TT.chksum,0)
--drop the temp tables from the tempdb
DROP TABLE #ChkSumTargetTables;
DROP TABLE #ChkSumSourceTables;
END
fonte
Respostas:
Aqui está o que eu fiz antes:
Funcionou bem o suficiente em tabelas com cerca de 1.000.000 de linhas, mas não tenho certeza de como isso funcionaria em tabelas extremamente grandes.
Adicionado:
Eu executei a consulta no meu sistema, que compara duas tabelas com 21 campos de tipos regulares em dois bancos de dados diferentes anexados ao mesmo servidor executando o SQL Server 2005. A tabela possui cerca de 3 milhões de linhas e há cerca de 25000 linhas diferentes. A chave primária na tabela é estranha, no entanto, pois é uma chave composta de 10 campos (é uma tabela de auditoria).
Os planos de execução para as consultas têm um custo total de 184.25879 para
UNION
e 184.22983 paraUNION ALL
. O custo da árvore difere apenas na última etapa antes de retornar as linhas, a concatenação.A execução de qualquer uma das consultas leva cerca de 42s mais cerca de 3s para realmente transmitir as linhas. O tempo entre as duas consultas é idêntico.
Segunda adição:
Na verdade, é extremamente rápido, cada um rodando contra 3 milhões de linhas em cerca de 2,5 segundos:
Se os resultados não corresponderem, você sabe que as tabelas são diferentes. No entanto, se os resultados do jogo, você não garantiu que os quadros sejam idênticos por causa da possibilidade [altamente improvável] de colisões de checksum.
Não tenho certeza de como as alterações no tipo de dados entre as tabelas afetariam esse cálculo. Eu executaria a consulta nos
system
pontos de vista ouinformation_schema
pontos de vista.Tentei a consulta em outra tabela com 5 milhões de linhas e essa foi executada em cerca de 5s, portanto parece ser em grande parte O (n).
fonte
EXCEPT
instruções. Na verdade, estou curioso agora, se é. No entanto, faz mais sentido lógico ser explícito no RDBMS, então eu o atualizarei.Aqui estão várias idéias que podem ajudar:
Experimente uma ferramenta de comparação de dados diferente - você já experimentou o conjunto de ferramentas de comparação de SQL da Idera ou o ApexSQL Data Diff . Sei que você já pagou pelo RG, mas ainda pode usá-los no modo de teste para concluir o trabalho;).
Dividir e conquistar - que tal dividir tabelas em 10 tabelas menores que podem ser manipuladas por alguma ferramenta comercial de comparação de dados?
Limite-se apenas a algumas colunas - você realmente precisa comparar dados em todas as colunas?
fonte
Acredito que você deva investigar BINARY_CHECKSUM, embora eu opte pela ferramenta Red Gate:
http://msdn.microsoft.com/en-us/library/ms173784.aspx
Algo assim:
fonte
Se você possui uma chave primária, essa é a melhor maneira de examinar as diferenças, pois as linhas que devem ser as mesmas são mostradas juntas.
Veja em um sqlfiddle .
fonte