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
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).
Voici plusieurs idées qui pourraient vous aider:
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;).
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?
Limitez-vous seulement à certaines colonnes - avez-vous vraiment besoin de comparer les données dans toutes les colonnes?
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;
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.*
);