Un moyen rapide de valider deux tables l'une par rapport à l'autre

12

Nous faisons un processus ETL. En fin de compte, il y a un tas de tableaux qui devraient être identiques. Quelle est la manière la plus rapide de vérifier que ces tables (sur deux serveurs différents) sont bien identiques. Je parle à la fois de schéma et de données.

Puis-je faire un hachage sur la table, c'est comme si je pouvais le faire sur un fichier individuel ou un groupe de fichiers - pour comparer l'un à l'autre. Nous avons des données Red-Gate à comparer, mais comme les tables en question contiennent chacune des millions de lignes, j'aimerais quelque chose d'un peu plus performant.

Une approche qui m'intrigue est cette utilisation créative de la déclaration syndicale . Mais, j'aimerais explorer l'idée de hachage un peu plus loin si possible.

MISE À JOUR APRÈS RÉPONSE

Pour tous les futurs visiteurs ... voici l'approche exacte que j'ai fini par adopter. Cela a si bien fonctionné que nous le faisons sur chaque table de chaque base de données. Merci aux réponses ci-dessous pour m'avoir pointé dans la bonne direction.

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
la source
SSIS est-il une option? Ce serait assez facile à lire dans un tableau et à faire une recherche dans l'autre.
Kevin
1
C'est une option, c'est ce qui est utilisé pour le processus ETL, mais les moustaches à l'étage veulent un deuxième avis sur si cela a fonctionné ou non. MD5 Hash.
RThomas

Réponses:

17

Voici ce que j'ai fait auparavant:

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

Cela a assez bien fonctionné sur des tables d'environ 1 000 000 de lignes, mais je ne sais pas dans quelle mesure cela fonctionnerait sur des tables extrêmement grandes.

Ajoutée:

J'ai exécuté la requête sur mon système qui compare deux tables avec 21 champs de types réguliers dans deux bases de données différentes attachées au même serveur exécutant SQL Server 2005. La table a environ 3 millions de lignes et il y a environ 25 000 lignes différentes. La clé primaire sur la table est cependant étrange, car c'est une clé composite de 10 champs (c'est une table d'audit).

Les plans d'exécution des requêtes ont un coût total de 184.25879 pour UNIONet 184.22983 pour UNION ALL. Le coût de l'arbre ne diffère que lors de la dernière étape avant le retour des lignes, la concaténation.

L'exécution de l'une ou l'autre requête prend environ 42 secondes et environ 3 secondes pour transmettre les lignes. Le temps entre les deux requêtes est identique.

Deuxième ajout:

C'est en fait extrêmement rapide, chacun fonctionnant contre 3 millions de lignes en environ 2,5 secondes:

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableA

SELECT CHECKSUM_AGG(BINARY_CHECKSUM(*)) FROM TableB

Si les résultats de ceux-ci ne correspondent pas, vous savez que les tableaux sont différents. Toutefois, si les résultats font match, vous n'êtes pas garanti que les tables sont identiques en raison de la chance [hautement improbable] des collisions de contrôle.

Je ne sais pas comment les changements de type de données entre les tables affecteront ce calcul. Je voudrais exécuter la requête sur les systemvues ou les information_schemavues.

J'ai essayé la requête contre une autre table avec 5 millions de lignes et celle-ci a fonctionné en environ 5 s, il semble donc être largement O (n).

Lardons
la source
8

Voici plusieurs idées qui pourraient vous aider:

  1. Essayez différents outils de diff de données - avez-vous essayé le jeu d'outils Comparaison SQL d'Idera ou ApexSQL Data Diff . Je me rends compte que vous avez déjà payé RG mais vous pouvez toujours les utiliser en mode d'essai pour faire le travail;).

  2. Diviser pour mieux régner - que diriez-vous de diviser des tables en 10 tables plus petites qui peuvent être gérées par un outil de comparaison de données commercial?

  3. Limitez-vous uniquement à certaines colonnes - avez-vous vraiment besoin de comparer les données de toutes les colonnes?

Mark Davidson
la source
7

Je pense que vous devriez enquêter sur BINARY_CHECKSUM, bien que j'opterais pour l'outil Red Gate:

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

Quelque chose comme ça:

SELECT BINARY_CHECKSUM(*) from myTable;
TelegraphOperator
la source
Cela détectera-t-il des différences dans le schéma des tables (différents noms de colonnes ou types de données)?
ypercubeᵀᴹ
3

Si vous avez une clé primaire, c'est parfois une meilleure façon d'examiner les différences car les lignes qui doivent être identiques sont affichées ensemble.

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

Voyez-le dans un sqlfiddle .

ErikE
la source