Estamos fazendo um processo de ETL. Quando tudo estiver dito e feito, há um monte de 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 por conta própria, como eu seria capaz de fazer em um arquivo individual ou grupo de arquivos - para comparar um com o outro. Temos comparação de dados do Red-Gate, mas como as tabelas em questão contêm milhões de linhas cada, gostaria de algo com um pouco mais de desempenho.
Uma abordagem que me intriga é esse uso criativo da declaração de união . Mas, eu gostaria de explorar a ideia de hash um pouco mais, se possível.
POSTAR RESPOSTA ATUALIZADA
Para qualquer visitante futuro... aqui está a abordagem exata que acabei tomando. Funcionou tão bem que estamos fazendo isso em todas as tabelas de 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
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 tem cerca de 3 milhões de linhas e há cerca de 25.000 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 das consultas tem 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.Na verdade, a execução de qualquer consulta 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, isso é extremamente rápido, cada um rodando contra 3 milhões de linhas em cerca de 2,5s:
Se os resultados não corresponderem, você sabe que as tabelas são diferentes. No entanto, se os resultados corresponderem, não há garantia de que as tabelas sejam idênticas devido à chance [altamente improvável] de colisões de soma de verificação.
Não tenho certeza de como as alterações de tipo de dados entre tabelas afetariam esse cálculo. Eu executaria a consulta nas
system
visualizações ouinformation_schema
visualizações.Eu tentei a consulta em outra tabela com 5 milhões de linhas e essa foi executada em cerca de 5s, então parece ser em grande parte O(n).
Aqui estão várias ideias que podem ajudar:
Experimente uma ferramenta de comparação de dados diferente - você já experimentou o conjunto de ferramentas de comparação de SQL do Idera ou o ApexSQL Data Diff . Percebo que você já pagou pelo RG, mas ainda pode usá-los no modo de teste para fazer o trabalho;).
Dividir e conquistar - que tal dividir as 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?
Acredito que você deve investigar BINARY_CHECKSUM, embora eu opte pela ferramenta Red Gate:
http://msdn.microsoft.com/en-us/library/ms173784.aspx
Algo assim:
Se você tiver uma chave primária, às vezes essa é uma maneira melhor de examinar as diferenças porque as linhas que devem ser iguais são mostradas juntas.
Veja em um sqlfiddle .