如何找出2个SQL表之间的内容差异并生成同步SQL


12

我如何找出具有精确模式的两个表之间的数据差异,以及如何生成同步SQL以获取并集结果(无重复项)?

这些是2个表:

SOURCE01.dbo.Customers (31,022 rows)

TARGET01.dbo.Customers (29,300 rows)

每个表的模式为:

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

Answers:


6

除了前面的答案中提到的tablediff和powershell之外,您还可以将SQL与UNION ALL语句一起使用,以查找两个相同表中不匹配的记录:

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;

您可以尝试的另一种选择是在Visual Studio本身中使用数据比较。它比较源数据库和目标数据库中的数据,并为您选择用于同步的表创建同步脚本。

最后但并非最不重要的一点是,您可以使用SQL数据比较工具-ApexSQL Data Diff来设置所有同步选项,使用不同的名称映射表和列,并在GUI中创建自己的比较键。您可以安排它在无人值守的情况下运行,而您所要做的就是早上检查SQL Server作业历史记录。如果您需要有关这些选项的更多详细信息,建议阅读本文:http : //solutioncenter.apexsql.com/automatically-compare-and-synchronize-sql-server-data/



4

使用本机工具:

tabledifftablediff实用程序将源表中的数据与目标表中的表进行比较。

powershell:比较对象使您可以实现这一目标。这是一个很好的例子

第三方:

redgate模式和数据比较。您甚至可以使用Powershell和模式/数据比较来使事情自动化。


3

最近,我将其用于类似目的:

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;

它确实依赖于主键的一致性。但是,毕竟您必须拥有一致的东西。生成类似上述代码的元脚本相对容易编写,并使多列表易于比较。

至于同步,您将必须source left join targettarget left join source,然后根据每个结果决定要执行的操作。


2

这应该为您提供两个表之间的差异,然后您可以将其包装在插入查询中,以将A的差异放入B中,反之亦然。

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

我们的免费工具之一为TableDiff提供了完整的界面:

http://nobhillsoft.com/Diana.aspx

另外,请查看我们的数据库比较工具。它是唯一一个可以比较无限量数据的服务器(其他任何一个都不能处理数百万条记录)…只要您在链接的两台服务器之间进行比较

http://nobhillsoft.com/NHDBCompare.aspx

(我们在该线程中看到了第三方产品的其他链接,因此我们相信提及我们的产品是合法的...请让我们知道是否是第三方)


2
AFAIK只要是真正问题的现场答案,它就是合法的,并且您声明您与该产品有联系。所以会以为很好。
马丁·史密斯

1

如果两个表都具有相似的主键,则可以使用以下策略比较源表和目标表:(我用星号标记了复合键列)

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

这是有效的,因为union隐式返回不同的记录。因此,对于您希望与目标中的源完全匹配的源中的任何给定行(由某个键标识),您都希望src和target的并集为任何给定键返回1行。因此,您可以使用上述策略找出哪些键返回具有多行的并集结果,然后再次查询src并集目标(这一次仅通过与diff表连接来选择具有差异的记录),然后选择您想要的所有列进行比较,然后按组成键的列进行排序,您会确切地看到哪些列不匹配。请注意,源名称和目标名称中的列名称不必匹配,因为可以使用“ as”语句将它们彼此别名。


0

要查找两个相同表之间的差异,请执行以下操作:

SELECT *
FROM SOURCE01.dbo.Customers

UNION

SELECT *
FROM TARGET01.dbo.Customers

EXCEPT

SELECT *
FROM SOURCE01.dbo.Customers

INTERSECT

SELECT *
FROM TARGET01.dbo.Customers


操作顺序使INTERSECT首先执行,这将为您提供仅两个表中都存在的行的数据集。其次,执行UNION,这将为您提供来自两个表的所有行,而不重复。最后,执行EXCEPT,从您的UNION(两个表的所有行)中删除INTERSECT数据集,这是两个表中的行。这样就为您提供了一个数据集,该数据集仅包含一个表中存在的行,而不包含另一个表中的行。如果您的数据集恢复为空,则表之间的所有行均相同。



https://docs.microsoft.com/zh-cn/sql/t-sql/language-elements/set-operators-except-and-intersect-transact-sql


嘿!我认为如果您使用原始问题中的表名,您的答案会更好!
安东尼·热那维斯

0

我有一个类似的问题,并使用SQL'EXCEPT'命令解决了该问题。EXCEPT命令采用两个SELECT语句,并返回第一个SELECT语句(左)而不是第二个(右)SELECT语句返回的行。

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

PS:SELECT语句返回的两个表的模式必须匹配。

为了更加清晰,请访问:此处的“指导点页面”


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)

该脚本(在提供相关详细信息时)比较2个表(例如,server1上的客户与Server2上的客户)。

如果您要比较具有许多列的表,但是很难找到确切的不匹配列,那么此脚本将很方便。

我有一个具有353列的表,我不得不将它与另一个表进行比较,并发现值不匹配,此脚本将帮助您找到确切的元组。


-1

我认为您应该尝试xSQL Data Compare,它可以解决您的问题。例如,假设您指定

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

之后你比较表,在对比结果,可以指定要同步只能从左边的表,它会产生一个SQL脚本插入到TARGET01.dbo.Customers所有行的差异并不在此表中,但存在于SOURCE01.dbo.Customers中(实现UNION结果而不重复)。希望这可以帮助!

披露:我隶属于xSQL。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.