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


12

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

Réponses:


6

Autre que tablediff et powershell mentionnés dans les réponses précédentes, vous pouvez également utiliser SQL avec l'instruction UNION ALL pour trouver 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, 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 sur ces options, je vous recommande de lire cet article: http://solutioncenter.apexsql.com/automatically-compare-and-synchronize-sql-server-data/



4

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.


3

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.

En ce qui concerne la synchronisation, vous devrez source left join targetet target left join source, puis décider ce que vous voulez faire avec le résultat de chacun.


2

Cela devrait vous donner les différences entre les deux tables, vous pouvez ensuite envelopper cela 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)

1

Un de nos outils gratuits a 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 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)


2
L'AFAIK est légitime tant qu'il s'agit d'une réponse ontopique à une véritable question et que vous déclarez avoir un lien avec le produit. J'aurais donc pensé que ça allait.
Martin Smith

1

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 une 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 savoir quelles clés renvoient un résultat d'union comportant plusieurs lignes, puis interroger à nouveau la cible d'union src (cette fois uniquement en sélectionnant les enregistrements présentant des différences en se joignant à la table diff) en sélectionnant toutes les colonnes que vous souhaitez comparer, ordonner par 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".


0

Pour trouver les différences entre deux tables identiques

SELECT *
DE SOURCE01.dbo.Customers

UNION

SELECT *
DE TARGET01.dbo.Customers

SAUF

SELECT *
FROM SOURCE01.dbo.Customers

INTERSECT

SELECT *
DE TARGET01.dbo.Customers


L'ordre des opérations fait que l'INTERSECT est exécuté en premier, ce qui vous donnera un ensemble de données de seulement 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 jeu 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


Héy! Je pense que votre réponse serait meilleure si vous utilisiez les noms de table de la question d'origine!
Anthony Genovese

0

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 renvoie les lignes qui sont renvoyé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

PS: le schéma des deux tables renvoyées par l'instruction SELECT doit correspondre.

Pour plus de clarté, visitez la page Point des didacticiels ici


0
/*
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 et Clients sur le serveur2).

Ce script sera pratique si vous comparez une table avec de nombreuses 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.


-1

Je pense que vous devriez essayer xSQL Data Compare , qui fera l'affaire dans votre cas. Disons par exemple que vous spécifiez

SOURCE01.dbo.Customers as the **left table** and
TARGET01.dbo.Customers as the **right table**

Après avoir comparé les tables, dans le résultat de la comparaison, vous pouvez spécifier que vous souhaitez synchroniser uniquement les différences de la table de gauche qui produirait un script SQL à insérer dans TARGET01.dbo.Customers toutes les lignes qui ne figurent pas dans cette table mais existent dans SOURCE01.dbo.Customers (Obtention d'un résultat UNION sans doublons). J'espère que cela t'aides!

Divulgation: je suis affilié à xSQL.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.