web-dev-qa-db-fra.com

Un moyen rapide de valider deux tables l'une contre l'autre

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 d'union . 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
14
RThomas

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é:

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 il s'agit d'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 UNION et 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 réellement 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. Cependant, si les résultats correspondent , vous n'êtes pas garantis que les tableaux sont identiques en raison du risque [hautement improbable] de collisions de sommes de contrôle.

Je ne sais pas comment les changements de type de données entre les tables affecteront ce calcul. Je lancerais la requête sur les vues system ou information_schema vues.

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

19
Bacon Bits

Voici plusieurs idées qui pourraient vous aider:

  1. Essayez différents outils de diff de données - avez-vous essayé jeu d'outils de comparaison SQL ou ApexSQL Data Diff d'Idera. Je me rends compte que vous avez déjà payé pour 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 seulement à certaines colonnes - avez-vous vraiment besoin de comparer les données dans toutes les colonnes?

8
Mark Davidson

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

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

Voir dans un sqlfiddle .

3
ErikE