Schnelle Möglichkeit, zwei Tabellen gegeneinander zu validieren

12

Wir machen einen ETL-Prozess. Letztendlich gibt es eine Reihe von Tabellen, die identisch sein sollten. Wie lässt sich am schnellsten überprüfen, ob diese Tabellen (auf zwei verschiedenen Servern) tatsächlich identisch sind? Ich spreche sowohl Schema als auch Daten.

Kann ich einen Hash auf dem Tisch ausführen, als wäre ich in der Lage, eine einzelne Datei oder Dateigruppe mit einer anderen zu vergleichen? Wir haben Red-Gate-Daten verglichen, aber da die fraglichen Tabellen jeweils Millionen von Zeilen enthalten, hätte ich gerne etwas Performanteres.

Ein Ansatz, der mich fasziniert, ist die kreative Verwendung der Gewerkschaftserklärung . Aber ich möchte die Hash-Idee, wenn möglich, ein wenig weiter untersuchen.

POST ANTWORT UPDATE

Für alle zukünftigen Besucher ... hier ist der genaue Ansatz, den ich letztendlich gewählt habe. Es hat so gut funktioniert, dass wir es für jede Tabelle in jeder Datenbank tun. Vielen Dank an die Antworten unten, die mich in die richtige Richtung gelenkt haben.

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
RThomas
quelle
Ist SSIS eine Option? Es wäre ziemlich einfach, in der einen Tabelle zu lesen und die andere nachzuschlagen.
Kevin
1
Es ist eine Option, es wird für den ETL-Prozess verwendet, aber die Schnurrbärte oben möchten eine zweite Meinung dazu, ob es funktioniert hat oder nicht. Mit SSIS zu beweisen, dass SSIS es richtig gemacht hat, ist nicht so überzeugend wie das Löschen ausgefallener Wörter wie CheckSum oder MD5 Hash.
RThomas

Antworten:

17

Folgendes habe ich bereits getan:

(SELECT 'TableA', * FROM TableA
EXCEPT
SELECT 'TableA', * FROM TableB)
UNION ALL
(SELECT 'TableB', * FROM TableB
EXCEPT
SELECT 'TableB', * FROM TableA)

Bei Tabellen mit etwa 1.000.000 Zeilen funktioniert es gut genug, aber ich bin mir nicht sicher, wie gut das bei extrem großen Tabellen funktionieren würde.

Hinzugefügt:

Ich habe die Abfrage auf meinem System ausgeführt, das zwei Tabellen mit 21 regulären Feldern in zwei verschiedenen Datenbanken vergleicht, die an denselben Server mit SQL Server 2005 angeschlossen sind. Die Tabelle enthält ungefähr 3 Millionen Zeilen und es gibt ungefähr 25000 verschiedene Zeilen. Der Primärschlüssel auf der Tabelle ist jedoch seltsam, da er aus 10 Feldern zusammengesetzt ist (es ist eine Prüftabelle).

Die Ausführungspläne für die Abfragen haben Gesamtkosten von 184.25879 für UNIONund 184.22983 für UNION ALL. Die Baumkosten unterscheiden sich nur im letzten Schritt vor der Rückgabe von Zeilen, der Verkettung.

Das Ausführen einer der beiden Abfragen dauert ungefähr 42 Sekunden plus ungefähr 3 Sekunden, um die Zeilen tatsächlich zu übertragen. Die Zeit zwischen den beiden Abfragen ist identisch.

Zweiter Zusatz:

Das ist eigentlich extrem schnell, jeder läuft gegen 3 Millionen Zeilen in ungefähr 2,5 Sekunden:

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableA

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableB

Wenn die Ergebnisse nicht übereinstimmen, wissen Sie, dass die Tabellen unterschiedlich sind. Stimmen die Ergebnisse jedoch überein, kann aufgrund der [höchst unwahrscheinlichen] Wahrscheinlichkeit von Prüfsummenkollisionen nicht garantiert werden, dass die Tabellen identisch sind.

Ich bin nicht sicher, wie sich Datentypänderungen zwischen Tabellen auf diese Berechnung auswirken würden. Ich würde die Abfrage gegen die systemAnsichten oder information_schemaAnsichten ausführen .

Ich habe versucht, die Abfrage für eine andere Tabelle mit 5 Millionen Zeilen durchzuführen, und diese wurde in etwa 5 Sekunden ausgeführt. Es scheint sich also weitgehend um O (n) zu handeln.

Speckwürfel
quelle
1
Warum schlagen Sie UNION anstelle von UNION ALL vor? Welche Duplikate möchten Sie beseitigen?
AK
@AlexKuznetsov Fair genug, obwohl ich vermute, dass die Abfrage-Engine klug genug ist, um zu erkennen, dass wir den gesamten Primärschlüssel auswählen, oder möglicherweise den Unterschied erst verarbeiten, wenn er die Ergebnismenge aus den EXCEPTAnweisungen aufweist. Ich bin jetzt wirklich gespannt, ob es so ist. Es ist jedoch logischer, für das RDBMS explizit zu sein, daher aktualisiere ich es.
Bacon Bits
Die Frage betrifft jedoch 2 verschiedene Server.
ypercubeᵀᴹ
Ich habe keine zwei Server zum Testen. :)
Bacon Bits
Die Gewerkschaft über einen Verbindungsserver-Ansatz hinweg hatte mich beunruhigt. Ich mag den Ansatz von aggregate und binary_checksum. Ich werde jetzt damit anfangen zu testen.
RThomas
8

Hier sind einige Ideen, die helfen könnten:

  1. Probieren Sie verschiedene Daten Diff - Tool - haben Sie versucht Idera SQL Vergleich Toolset oder ApexSQL Daten Diff . Mir ist klar, dass Sie bereits für RG bezahlt haben, aber Sie können diese weiterhin im Testmodus verwenden, um die Arbeit zu erledigen;).

  2. Teilen und erobern - wie wäre es, wenn Sie Tabellen in 10 kleinere Tabellen aufteilen, die von einem kommerziellen Datenvergleichstool verarbeitet werden können?

  3. Beschränken Sie sich auf einige Spalten. Müssen Sie wirklich Daten in allen Spalten vergleichen?

Mark Davidson
quelle
7

Ich glaube, Sie sollten BINARY_CHECKSUM untersuchen, obwohl ich mich für das Red Gate-Tool entscheiden würde:

http://msdn.microsoft.com/en-us/library/ms173784.aspx

Etwas wie das:

SELECT BINARY_CHECKSUM(*) from myTable;
TelegraphOperator
quelle
Wird dies Unterschiede im Schema der Tabellen erkennen (unterschiedliche Spaltennamen oder Datentypen)?
ypercubeᵀᴹ
Das hat Potenzial. Ich werde ein paar Tests machen und zurückkommen.
RThomas
Wir haben Ihnen ein Plus von 1 gegeben, aber die Summe der Speckstücke war das i-Tüpfelchen. Vielen Dank.
RThomas
3

Wenn Sie einen Primärschlüssel haben, ist dies manchmal eine bessere Möglichkeit, Unterschiede zu untersuchen, da die Zeilen, die gleich sein sollten, zusammen angezeigt werden.

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

Sehen Sie es in einer SQL-Geige .

ErikE
quelle
Schön, danke ... gute Möglichkeit, Unterschiede zu finden, nachdem das Aggregat anzeigt, dass Unterschiede bestehen. Vielen Dank.
RThomas