Děláme ETL proces. Když je vše řečeno a hotovo, existuje spousta tabulek, které by měly být totožné. Jaký je nejrychlejší způsob, jak ověřit, že tyto tabulky (na dvou různých serverech) jsou ve skutečnosti identické. Mluvím jak o schématu, tak o datech.
Mohu udělat hash na stole je to samé, jako bych byl schopen na jednotlivý soubor nebo skupinu souborů - porovnat jeden s druhým. Srovnáváme data Red-Gate, ale protože dotyčné tabulky obsahují miliony řádků, každý by chtěl něco trochu výkonnějšího.
Jedním z přístupů, který mě zaujme, je toto kreativní využití prohlášení odborů . Ale rád bych prozkoumal hašovací myšlenku o něco dále, pokud je to možné.
AKTUALIZACE POŠTOVNÍ ODPOVĚDI
Pro všechny budoucí vistory ... tady je přesný přístup, který jsem nakonec použil. Fungovalo to tak dobře, že to děláme na každé tabulce v každé databázi. Díky níže uvedeným odpovědím, že mě směřujete správným směrem.
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
Tady je to, co jsem udělal předtím:
(SELECT 'TableA', * FROM TableA
EXCEPT
SELECT 'TableA', * FROM TableB)
UNION ALL
(SELECT 'TableB', * FROM TableB
EXCEPT
SELECT 'TableB', * FROM TableA)
Funguje to dost dobře na stolech, které mají asi 1 000 000 řádků, ale nejsem si jistý, jak dobře by to fungovalo na extrémně velkých stolech.
Přidáno:
Spustil jsem dotaz proti svému systému, který porovnává dvě tabulky s 21 poli běžných typů ve dvou různých databázích připojených ke stejnému serveru se systémem SQL Server 2005. Tabulka obsahuje asi 3 miliony řádků a liší se přibližně 25 000 řádků. Primární klíč v tabulce je však zvláštní, protože se jedná o složený klíč z 10 polí (je to kontrolní tabulka).
Plány provádění dotazů mají celkové náklady 184,255879 pro UNION
a 184,228383 pro UNION ALL
. Cena stromu se liší pouze v posledním kroku před vrácením řádků, zřetězením.
Skutečné provedení jednoho dotazu trvá asi 42 s plus asi 3 s, než se řádky skutečně přenesou. Čas mezi dvěma dotazy je stejný.
Druhý dodatek:
Toto je ve skutečnosti extrémně rychlé, každý z nich běží proti 3 milionům řádků přibližně za 2,5 s:
SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableA
SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableB
Pokud se výsledky těchto neshodují, víte, že tabulky jsou odlišné. Pokud se však výsledky shodují , nemáte zaručeno, že tabulky jsou totožné kvůli [velmi nepravděpodobné] možnosti kolize kontrolního součtu.
Nejsem si jistý, jak změny datového typu mezi tabulkami ovlivní tento výpočet. Spustil bych dotaz proti zobrazením system
nebo information_schema
zobrazení.
Zkoušel jsem dotaz proti jiné tabulce s 5 miliony řádků a ten běžel asi za 5 s, takže se zdá být převážně O (n).
Zde je několik nápadů, které mohou pomoci:
Vyzkoušejte jiný datový rozdíl - vyzkoušeli jste Ideru sada nástrojů SQL Comparison nebo ApexSQL Data Diff . Uvědomuji si, že jste již zaplatili za RG, ale stále je můžete použít v zkušebním režimu, abyste dokončili svou práci;).
Rozdělit a dobýt - co takhle rozdělení tabulek na 10 menších tabulek, které lze zpracovat pomocí nějakého nástroje pro porovnávání komerčních dat?
Omezte se pouze na některé sloupce - opravdu potřebujete porovnat data ve všech sloupcích?
Věřím, že byste měli prozkoumat BINARY_CHECKSUM, i když bych se rozhodl pro nástroj Red Gate:
http://msdn.Microsoft.com/en-us/library/ms173784.aspx
Něco takového:
SELECT BINARY_CHECKSUM(*) from myTable;
Pokud máte primární klíč, je to někdy lepší způsob, jak prozkoumat rozdíly, protože řádky, které by měly být stejné, jsou zobrazeny společně.
SELECT
ID = IsNull(A.ID, B.ID),
AValue = A.Value,
BValue = B.Value
FROM
dbo.TableA A
FULL JOIN dbo.TableB B
ON A.ID = B.ID
WHERE
EXISTS (
SELECT A.*
EXCEPT SELECT B.*
);