Como descobrir as diferenças de conteúdo entre 2 tabelas SQL e produzir sincronização SQL

12

Como descubro as diferenças de dados entre as duas tabelas que possuem esquema exato e como produzir SQL de sincronização para obter os resultados da união (sem duplicatas)?

Estas são as 2 tabelas:

SOURCE01.dbo.Customers (31,022 rows)

TARGET01.dbo.Customers (29,300 rows)

O esquema de cada tabela é:

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

Respostas:

6

Além de tablediff e powershell mencionados nas respostas anteriores, você também pode usar o SQL com a instrução UNION ALL para encontrar os registros que não correspondem em 2 tabelas idênticas:

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;

Outra opção que você pode tentar é usar a Comparação de dados no próprio Visual Studio. Ele compara os dados no banco de dados de origem e de destino e cria um script de sincronização para as tabelas que você selecionou para sincronização.

E por último, mas não menos importante, você pode usar a ferramenta de comparação de dados SQL - ApexSQL Data Diff , para definir todas as opções de sincronização, mapear as tabelas e colunas com nomes diferentes, criar suas próprias chaves para comparação na GUI. Você pode agendá-lo para execução autônoma e tudo o que você precisa fazer é verificar o histórico de tarefas do SQL Server pela manhã. Se você precisar de mais detalhes sobre essas opções, recomendo a leitura deste artigo: http://solutioncenter.apexsql.com/automatically-compare-and-synchronize-sql-server-data/

Andreas Voller
fonte
4

Usando ferramentas nativas:

tablediff : o utilitário tablediff compara os dados na tabela de origem com a tabela na tabela de destino.

powershell: compare-object permite que você consiga isso. Aqui está um bom exemplo

terceiro:

esquema do redgate e comparação de dados. Você pode até usar o PowerShell e a comparação de esquema / dados para automatizar as coisas.

Kin Shah
fonte
3

Eu usei este recentemente para uma finalidade semelhante:

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;

Depende da chave primária ser consistente. Mas você precisa ter algo consistente, afinal. Um meta script para gerar código como o acima é relativamente fácil de escrever e facilita a comparação de tabelas de várias colunas.

Quanto à sincronização, você precisará source left join targete target left join source, em seguida, decida o que deseja fazer com o resultado de cada um.

Michael Green
fonte
2

Isso deve fornecer as diferenças entre as duas tabelas. Em seguida, você pode agrupar isso em uma consulta de inserção para colocar as diferenças de A em 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)
Reaces
fonte
1

Uma de nossas ferramentas gratuitas possui uma interface completa para o TableDiff:

http://nobhillsoft.com/Diana.aspx

Além disso, confira nossa ferramenta de comparação de banco de dados. É o único por aí que compara uma quantidade ilimitada de dados (outros não podem fazer milhões e milhões de registros) ... desde que você compare entre dois servidores vinculados

http://nobhillsoft.com/NHDBCompare.aspx

(vimos outros links neste tópico para produtos de terceiros, por isso acreditamos que é legítimo mencionar o nosso ... por favor, deixe-nos saber se não é)

Jonathan Scion
fonte
2
O AFAIK é legítimo desde que seja uma resposta ontopica a uma pergunta genuína e você declara ter uma conexão com o produto. Então, teria pensado que está tudo bem.
Martin Smith
1

Se ambas as tabelas tiverem chaves primárias semelhantes, você poderá usar a estratégia abaixo para comparar as tabelas de origem e de destino: (marquei as colunas de chaves compostas com um asterik)

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

Isso funciona porque a união retorna implicitamente registros distintos. Portanto, para qualquer linha (identificada por alguma chave) na fonte que você espera corresponder exatamente no destino, você esperaria que uma união do src e do destino retornasse 1 linha para qualquer chave. Portanto, você pode usar a estratégia acima para descobrir quais chaves retornam um resultado de união com várias linhas e, em seguida, consultar o destino da união src novamente (desta vez, apenas selecionando os registros com diferenças, juntando-se à tabela diff), selecionando todas as colunas que você deseja compare, ordenando pelas colunas que compõem a chave e você verá exatamente quais colunas são incompatíveis. Observe que os nomes das colunas na origem e no destino não precisam corresponder, pois podem ser alternados entre si usando uma instrução "as".

mancini0
fonte
0

Para encontrar as diferenças entre duas tabelas idênticas

SELECT *
FROM SOURCE01.dbo.Customers

UNION

SELECT *
FROM TARGET01.dbo.Customers

EXCEPT

SELECT *
FROM SOURCE01.dbo.Customers

INTERSECT

SELECT *
FROM TARGET01.dbo.Customers


A ordem das operações faz com que o INTERSECT seja executado primeiro, fornecendo um conjunto de dados com apenas as linhas existentes nas duas tabelas. Segundo, o UNION é executado, fornecendo todas as linhas de ambas as tabelas sem duplicatas. Finalmente, é executado o EXCEPT, que exclui do UNION (todas as linhas de ambas as tabelas) o conjunto de dados INTERSECT, que são as linhas das duas tabelas. Isso deixa você com um conjunto de dados que contém apenas as linhas que existem em uma das tabelas, mas não na outra. Se o seu conjunto de dados voltar vazio, todas as linhas serão iguais entre as tabelas.



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

mihalko
fonte
Olá! Acho que sua resposta seria melhor se você usasse os nomes das tabelas da pergunta original!
Anthony Genovese
0

Eu tive um problema semelhante e usei o comando SQL 'EXCEPT' para resolver o problema. O comando EXCEPT utiliza duas instruções SELECT e retorna as linhas retornadas pela primeira instrução SELECT (esquerda) e não pela segunda instrução SELECT (direita).

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

PS: O esquema para as duas tabelas retornadas pela instrução SELECT deve corresponder.

Para mais clareza, visite: Página de Tutoriais aqui

avirup.m97
fonte
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)

O script (quando fornecido com os detalhes relevantes) compara 2 tabelas (diga Clientes no servidor1 a Clientes no Servidor2).

Esse script será útil se você estiver comparando uma tabela com muitas colunas, mas se esforçar para encontrar a coluna incompatível exata.

Eu tenho uma tabela com 353 colunas e tive que compará-la com outra tabela e encontrar valores que não correspondiam e esse script ajudará a localizar a tupla exata.

user191127
fonte
-1

Eu acho que você deve tentar o xSQL Data Compare , que fará o truque no seu caso. Digamos, por exemplo, que você especifique

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

Depois de comparar as tabelas, no resultado da comparação, é possível especificar que você deseja sincronizar apenas as diferenças da tabela esquerda que produziriam um script SQL para inserir no TARGET01.dbo.Customers todas as linhas que não estão nesta tabela, mas existe em SOURCE01.dbo.Customers (alcançando um resultado UNION sem duplicatas). Espero que isto ajude!

Divulgação: sou afiliado ao xSQL.

Endi Zhupani
fonte