it-swarm-eu.dev

Rychlý způsob ověření dvou tabulek proti sobě

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
14
RThomas

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).

19
Bacon Bits

Zde je několik nápadů, které mohou pomoci:

  1. 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;).

  2. 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?

  3. Omezte se pouze na některé sloupce - opravdu potřebujete porovnat data ve všech sloupcích?

8
Mark Davidson

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;
7

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.*
   );

Viz to v sqlfiddle .

3
ErikE