web-dev-qa-db-fra.com

Comment trouver les différences de contenu entre 2 tables SQL et produire du SQL de synchronisation

Comment puis-je trouver les différences de données entre les deux tables qui ont un schéma exact, et comment produire du SQL de synchronisation pour obtenir les résultats de l'union (sans doublons)?

Ce sont les 2 tableaux:

SOURCE01.dbo.Customers (31,022 rows)

TARGET01.dbo.Customers (29,300 rows)

Le schéma de chaque table est:

  • [CustomerId] : nvarchar(255)
  • [CustomerSerializedProfile]: nvarchar(max)
  • [CreatedDatetime] : DateTime
13
Dio Phung

Outre tablediff et powershell mentionnés dans les réponses précédentes, vous pouvez également utiliser SQL avec l'instruction UNION ALL pour rechercher les enregistrements qui ne correspondent pas dans 2 tables identiques:

SELECT MIN(TableName) AS TableName
   ,ID
   ,NAME
   ,lastname
   ,Address
   ,City
FROM (
SELECT 'Table A' AS TableName
    ,Customers.id
    ,Customers.NAME
    ,Customers.lastname
    ,Customers.Address
    ,Customers.City
FROM Customers

UNION ALL

SELECT 'Table B' AS TableName
    ,CustomersOld.id
    ,CustomersOld.NAME
    ,CustomersOld.lastname
    ,CustomersOld.Address
    ,CustomersOld.City
FROM CustomersOld
) tmp
GROUP BY ID
   ,NAME
   ,lastname
   ,Address
   ,City
HAVING COUNT(*) = 1
ORDER BY id;

Une autre option que vous pouvez essayer est d'utiliser la comparaison de données dans Visual Studio lui-même. Il compare les données de la base de données source et de la base de données cible et crée un script de synchronisation pour les tables que vous avez sélectionnées pour la synchronisation.

Et enfin, mais non des moindres, vous pouvez utiliser l'outil de comparaison de données SQL - ApexSQL Data Diff , pour définir toutes les options de synchronisation, mapper les tables et les colonnes avec des noms différents, créer vos propres clés de comparaison dans l'interface graphique . Vous pouvez planifier son exécution sans surveillance et tout ce que vous avez à faire est de vérifier l'historique des travaux SQL Server le matin. Si vous avez besoin de plus de détails concernant ces options, je vous recommande de lire cet article: http://solutioncenter.apexsql.com/automatically-compare-and-synchronize-sql-server-data/

7
Andreas Voller

De façon assez surprenante, personne n'a encore mentionné que cela est intégré aux outils de données SQL Server. Bien que la fonctionnalité soit basique par rapport à Redgate par exemple.

Quelques détails dans Comparer et synchroniser des données dans une ou plusieurs tables avec des données dans une base de données de référence

6
Martin Smith

Utilisation d'outils natifs:

tablediff : l'utilitaire tablediff compare les données de la table source à la table de la table de destination.

powershell: compare-object vous permet d'atteindre cet objectif. voici un bon exemple

tierce personne:

le schéma Redgate et la comparaison des données. Vous pouvez même utiliser PowerShell et comparer schéma/données pour automatiser les choses.

4
Kin Shah

J'ai récemment utilisé celui-ci dans un but similaire:

select
    s.*
    ,t.*
from SOURCE01.dbo.Customers as s
full outer join TARGET01.dbo.Customers as t
    on s.CustomerId = t.CustomerId
where s.CustomerSerializedProfile <> t.CustomerSerializedProfile
or s.CreatedDatetime <> t.CreatedDatetime
or s.CustomerId is NULL
or t.CustomerId is NULL;

Il repose sur la cohérence de la clé primaire. Mais vous devez avoir quelque chose de cohérent après tout. Un méta script pour générer du code comme ci-dessus est relativement facile à écrire et rend les tableaux à plusieurs colonnes faciles à comparer.

Quant à la synchronisation, vous devrez source left join target et target left join source, puis décidez de ce que vous voulez faire du résultat de chacun.

3
Michael Green

Cela devrait vous donner les différences entre les deux tables, vous pouvez ensuite envelopper dans une requête d'insertion pour mettre les différences de A en B ou vice versa.

SELECT A.CustomerId, A.CustomerSerializedProfile, A.CreatedDatetime
  FROM SOURCE01.dbo.Customers A
 WHERE NOT EXISTS (SELECT B.ID
                 FROM TARGET01.dbo.Customers
                WHERE B.CustomerId= A.CustomerId
                  AND B.CustomerSerializedProfile= A.CustomerSerializedProfile
                  AND B.CreatedDatetime= A.CreatedDatetime)
2
Reaces

L'un de nos outils gratuits dispose d'une interface complète pour TableDiff:

http://nobhillsoft.com/Diana.aspx

Consultez également notre outil de comparaison de bases de données. C'est le seul qui compare une quantité illimitée de données (aucun des autres ne peut faire des millions et des millions d'enregistrements)… tant que vous comparez entre 2 serveurs qui sont liés

http://nobhillsoft.com/NHDBCompare.aspx

(nous avons vu d'autres liens dans ce fil pour les produits tiers, nous pensons donc qu'il est légitime de mentionner le nôtre ... veuillez nous le faire savoir si ce n'est pas le cas)

1
Jonathan Scion

Si les deux tables ont des clés primaires similaires, vous pouvez utiliser la stratégie ci-dessous pour comparer les tables source et cible: (J'ai marqué les colonnes de clé composite avec un astérisque)

with src as (select someCol1*, 
                    someCol2*, 
                    someCol3, 
                    someCol4, 
                    someCol5
             from src_table),

tgt as (select someCol1NameCouldDiffer* as someCol1, 
               someCol2*, 
               someCol3, 
               someCol4, 
               someCol5
        from tgt_table),

--Find which keys have at least 1 non-key column difference:

diffs as (select someCol1, 
                 someCol2 
          from (select all 5 columns 
                from src 
                **union** 
                select all 5 columns 
                from target ) 
           **group by** someCol1, someCol2 
           **having count(*)>1** 

--Reselect all columns you wish to compare from src union target, 
--joining on the keys from "diffs" above to show only records which 
--have data differences.

select * 
from (select all 5 columns 
      from src 
      union 
      select all 5 cols 
       from tgt) t1 
join diffs on t1.someCol1 = diffs.someCol1 
           and t1.someCol2 = diffs.someCol2 
**order by ** someCol1, someCol2 desc

Cela fonctionne car l'union renvoie implicitement des enregistrements distincts. Donc, pour toute ligne donnée (identifiée par une clé) dans la source que vous attendez pour correspondre exactement dans la cible, vous vous attendriez à ce qu'une union du src et de la cible renvoie 1 ligne pour une clé donnée. Ainsi, vous pouvez utiliser la stratégie ci-dessus pour découvrir quelles clés renvoyer un résultat d'union ayant plusieurs lignes, puis interroger à nouveau la cible d'union src, (cette fois uniquement en sélectionnant les enregistrements ayant des différences en se joignant à la table diff. ) en sélectionnant toutes les colonnes que vous souhaitez comparer, en les classant selon les colonnes composant la clé, et vous verrez exactement quelles colonnes ne correspondent pas. Notez que les noms de colonne dans la source et la cible ne doivent pas nécessairement correspondre, car ils peuvent être aliasés l'un à l'autre à l'aide d'une instruction "as".

1
mancini0

J'ai eu un problème similaire et j'ai utilisé la commande SQL 'EXCEPT' pour résoudre le problème. La commande EXCEPT prend deux instructions SELECT et retourne les lignes qui sont retournées par la première instruction SELECT (à gauche) et non par la seconde (SELECT) instruction SELECT.

SELECT * from table1 where x,y,z 
EXCEPT
SELECT * from table2 where a,b,c

P.S: Le schéma des deux tables renvoyées par l'instruction SELECT doit correspondre.

Pour plus de clarté, visitez: Page Tutoriels Point ici

0
avirup.m97
/*
Compare master table data on 2 servers (
1. Change server name
2. Set RaceDate (@racedate) with the >, < ,= >= operator 
 before you run)

 --KNOWN ISSUES
 1. Tables need PKs

*/
SET NOCOUNT ON

--Destination Server Details
DECLARE @destServ nvarchar(40)='[sql\inst23]'    --required             -- If local instance, leave the string empty 
DECLARE @destdb nvarchar(40)='DBName'         --required        
DECLARE @destSchema nvarchar(40)='dbo'        --required        
DECLARE @destTable  nvarchar(40)='TableName'    --required      

-- Source Server Details
DECLARE @SourServ nvarchar(40)='[sql\inst07]'   --required      
DECLARE @Sourdb nvarchar(40)='DBonRemoteServer'  --required     
DECLARE @SourSchema nvarchar(40)='dbo'          --required      
DECLARE @SourTable  nvarchar(40)='TableName'      --required                                -- TableName format 'MyTable'

DECLARE @WHERE nvarchar(400) = 'WHERE 1=1'

DECLARE @Clause nvarchar(400)= 'AND Id > 201808201500000'       --Choose a Predicate to limit data --Start with AND . e.g: 'AND Date > ''20180801'' '

SELECT @WHERE = @WHERE + @Clause

DECLARE @randomtablesuffix nvarchar(5)
SELECT @randomtablesuffix= SUBSTRING(CAST(NEWID() as nvarchar(255)),1,5)


declare @v nvarchar(max), @sql nvarchar(max), @retval nvarchar(max) , @ParamDef nvarchar(400)

--GET Columns List as varchar Columns for HASHBYTES to compare
SELECT @sql='SELECT @vv= COALESCE(@vv,'''')+''CAST(ISNULL(''+ COLUMN_NAME  + '',0) as VARCHAR(''+ 
        CASE WHEN DATA_TYPE IN (''varchar'',''nvarchar'') THEN CAST(CHARACTER_MAXIMUM_LENGTH as varchar(5)) ELSE ''60 '' END +'')) + ''
from '+ @destdb + '.INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='+ QUOTENAME(@destTable,'''') + ''

SET @ParamDef = N'@vv nvarchar(max) OUTPUT'
EXEC sp_executesql @sql, @ParamDef, @vv=@v OUTPUT;

SELECT @v= SUBSTRING(@v,0,LEN(@v))

--Keys to JOIN
DECLARE @pkeylistJoinOUT nvarchar(4000)=''
SET @sql='SELECT @pkeylistJoin = ISNULL(@pkeylistJoin,'''') + '' a.''+ QUOTENAME(COLUMN_NAME) + ''=b.''+ QUOTENAME(COLUMN_NAME) + '' AND'' 
    FROM '+@destdb+'.[INFORMATION_SCHEMA].[KEY_COLUMN_USAGE]
    WHERE TABLE_NAME='+ QUOTENAME(@destTable,'''') + ' ORDER BY ORDINAL_POSITION'

SET @ParamDef = N'@pkeylistJoin nvarchar(max) OUTPUT'
EXEC sp_executesql @sql, @ParamDef, @pkeylistJoin=@pkeylistJoinOUT OUTPUT;  

SELECT @pkeylistJoinOUT = REPLACE(REPLACE(REVERSE( SUBSTRING(REVERSE(@pkeylistJoinOUT), CHARINDEX(']', REVERSE(@pkeylistJoinOUT)), LEN(@pkeylistJoinOUT)) ),']',''),'[','')


--Get Column List 

DECLARE @ColumnListOut nvarchar(max)=''
SET @sql='SELECT  @ColumnList=ISNULL(@ColumnList,'''') + COLUMN_NAME + '',''  FROM '+@destdb +'.[INFORMATION_SCHEMA].[COLUMNS] WHERE TABLE_NAME='+QUOTENAME(@destTable,'''')+ ' ORDER BY ORDINAL_POSITION'

SET @ParamDef = N'@ColumnList nvarchar(max) OUTPUT'
EXEC sp_executesql @sql, @ParamDef, @ColumnList=@ColumnListOut OUTPUT;  


SET @ColumnListOut=SUBSTRING(@ColumnListOut,0,LEN(@ColumnListOut))

--Now Compare

SELECT @sql='

SELECT a.* INTO ##_destissues'+@randomtablesuffix+' FROM (
SELECT HASHBYTES (''SHA2_512'','+ @v +')HashVal,'+ @ColumnListOut +' FROM '+@destServ+'.'+@destdb+'.'+@destSchema+'.'+@destTable + ' x WITH (NOLOCK) ' + @WHERE + '
)a 
JOIN (
SELECT HASHBYTES (''SHA2_512'','+@v +')HashVal,'+ @ColumnListOut + ' FROM ' +@SourServ +'.'+ @Sourdb+ '.'+@SourSchema+'.'+ @SourTable +' y WITH (NOLOCK)  ' + @WHERE + '
)
b ON '+@pkeylistJoinOUT + ' AND  a.HashVal <> b.HashVal '

--print @sql

exec (@sql)


SELECT @sql='

SELECT b.* INTO ##_sourceissues'+@randomtablesuffix+ ' FROM (
SELECT HASHBYTES (''SHA2_512'','+ @v +')HashVal,'+ @ColumnListOut +' FROM '+@destServ+'.'+@destdb+'.'+@destSchema+'.'+@destTable + ' x WITH (NOLOCK) ' + @WHERE + '
)a 
JOIN (
SELECT HASHBYTES (''SHA2_512'','+@v +')HashVal,'+ @ColumnListOut + ' FROM ' +@SourServ +'.'+ @Sourdb+ '.'+@SourSchema+'.'+ @SourTable +' y WITH (NOLOCK)  ' + @WHERE + '
)
b ON '+@pkeylistJoinOUT + ' AND  a.HashVal <> b.HashVal '


exec (@sql)

--Get Column List for Pivoting
DECLARE @ColumnListOutasVC nvarchar(max)=''

SET @sql='SELECT  @ColumnList=ISNULL(@ColumnList,'''')+  ''CAST(''+ COLUMN_NAME + '' AS VARCHAR(200)) as ''+ COLUMN_NAME + '',''   FROM ' + @destdb+'.[INFORMATION_SCHEMA].[COLUMNS] WHERE TABLE_NAME='+QUOTENAME(@desttable,'''')


SET @ParamDef = N'@ColumnList nvarchar(max) OUTPUT'
EXEC sp_executesql @sql, @ParamDef, @ColumnList=@ColumnListOutasVC OUTPUT;  

SET @ColumnListOutasVC=SUBSTRING(@ColumnListOutasVC,0,LEN(@ColumnListOutasVC))

--Get PKs as VARCHAR Values

DECLARE @pkeylistJoinOUTVC nvarchar(4000)=''

SET @sql='SELECT @pkeylistJoin = ISNULL(@pkeylistJoin,'''') + ''CAST(''+COLUMN_NAME + '' as varchar(200)) as '' + COLUMN_NAME + ''1,''  FROM '+ @destdb+'.[INFORMATION_SCHEMA].[KEY_COLUMN_USAGE]   WHERE TABLE_NAME='+QUOTENAME(@destTable,'''') + '  ORDER BY ORDINAL_POSITION'

SET @ParamDef = N'@pkeylistJoin nvarchar(max) OUTPUT'
EXEC sp_executesql @sql, @ParamDef, @pkeylistJoin=@pkeylistJoinOUTVC OUTPUT;    
SET @pkeylistJoinOUTVC=SUBSTRING(@pkeylistJoinOUTVC,0,LEN(@pkeylistJoinOUTVC))
--SELECT @pkeylistJoinOUTVC





SET @sql='
select  * INTO ##_destissuedetail'+@randomtablesuffix+ ' from(
select '+ @pkeylistJoinOUTVC + ', ' + @ColumnListOutasVC
                + '
from 
##_destissues'+ @randomtablesuffix+ '
)c UNPIVOT
(
Vals for ColNames in ('+@ColumnListOut+')
) d'

EXEC( @sql)


SET @sql='
select  * INTO ##_sourceissuedetail'+@randomtablesuffix+' from(
select '+ @pkeylistJoinOUTVC + ', ' + @ColumnListOutasVC
                + '
from 
##_sourceissues'+ @randomtablesuffix+'
)c UNPIVOT
(
Vals for ColNames in ('+@ColumnListOut+')
) d'

EXEC( @sql)

SELECT 'Tables to look for data are ##_destissuedetail'+@randomtablesuffix +' and  ##_sourceissuedetail ' +@randomtablesuffix

SET @sql='
SELECT * FROM ##_destissuedetail'+@randomtablesuffix+ '
EXCEPT
SELECT * FROM ##_sourceissuedetail' +@randomtablesuffix

EXEC (@sql)

Le script (lorsqu'il est fourni avec les détails pertinents) compare 2 tables (par exemple Clients sur le serveur1 à Clients sur le serveur2).

Ce script sera pratique si vous comparez une table avec plusieurs colonnes mais que vous avez du mal à trouver la colonne exacte qui ne correspond pas.

J'ai une table avec 353 colonnes et j'ai dû la comparer à une autre table et trouver avec des valeurs qui ne correspondaient pas et ce script vous aidera à localiser le tuple exact.

0
user191127

Pour trouver les différences entre deux tables identiques

SELECT *
FROM SOURCE01.dbo.Clients

SYNDICAT

SELECT *
DE TARGET01.dbo.Clients

SAUF

SELECT *
FROM SOURCE01.dbo.Clients

COUPER

SELECT *
DE TARGET01.dbo.Clients


L'ordre des opérations entraîne la première exécution d'INTERSECT, ce qui vous donnera un ensemble de données contenant uniquement les lignes qui existent dans les deux tables. Deuxièmement, l'UNION est effectuée, ce qui vous donne toutes les lignes des deux tables sans doublons. Enfin, l'EXCEPT est effectué qui supprime de votre UNION (toutes les lignes des deux tables) l'ensemble de données INTERSECT qui est les lignes des deux tables. Cela vous laisse avec un ensemble de données contenant uniquement les lignes qui existent dans l'une des tables mais pas dans l'autre. Si votre jeu de données revient vide, toutes les lignes sont identiques entre les tables.



https://docs.Microsoft.com/en-us/sql/t-sql/language-elements/set-operators-except-and-intersect-transact-sql

0
mihalko