SQL compara dados de duas tabelas

88

Eu tenho 2 tabelas TableAe TableBque têm o mesmo formato de coluna, por exemplo ambas as tabelas TableAe TableBtêm colunas

A B C D E F 

onde A e B são as chaves primárias.

Como escrever SQL para verificar se se TableAe TableBisso têm as mesmas chaves primárias contém exatamente o mesmo valor em todas as colunas.

Isso significa que essas duas tabelas têm exatamente os mesmos dados.

Nikky
fonte

Respostas:

81

Você deve ser capaz de "MINUS" ou "EXCEPT" dependendo do tipo de SQL usado por seu DBMS.

select * from tableA
minus
select * from tableB

Se a consulta não retornar nenhuma linha, os dados serão exatamente os mesmos.

dietbuddha
fonte
5
Excelente sugestão. No entanto, acho que isso pode não funcionar se a tabela B tiver uma (s) linha (s) extra (s), então você pode querer comparar as contagens de linha também.
jzd
5
Contrário. Não funcionará se tableAtiver linhas extras. Você precisaria, (A EXCEPT B) INTERSECT (B EXCEPT A)eu acho que isso seria muito menos eficiente do que uma junção padrão de bog.
Martin Smith
a consulta retorna dois conjuntos de resultados ??
BuZz
Esta consulta retornará linhas com NULLS, se houver.
Reeya Oberoi
5
@Franklin - Não, deve retornar apenas um conjunto de resultados. Se você estiver recebendo dois, use EXCEPT em vez de MENUS.
MTS de
56

Usando operadores relacionais:

SELECT * FROM TableA
UNION 
SELECT * FROM TableB
EXCEPT 
SELECT * FROM TableA
INTERSECT
SELECT * FROM TableB;

Mude EXCEPTpara MINUSpara Oracle.

Ponto um pouco exigente: o acima depende da precedência do operador, que de acordo com o padrão SQL é dependente da implementação, portanto, YMMV. Funciona para SQL Server, para o qual a precedência é:

  1. Expressões entre parênteses
  2. INTERSECT
  3. EXCEPTe UNIONavaliados da esquerda para a direita.
um dia quando
fonte
Para Oracle, você precisa usar parênteses em torno de UNION, parênteses em torno de INTERSECT e (conforme observado) substituir EXCEPT por MENUS. HTH.
Doug Clutter
20

dietbuddha tem uma boa resposta. Nos casos em que você não tem MENUS ou EXCEPT, uma opção é fazer uma união entre todas as tabelas, agrupar com todas as colunas e garantir que haja dois de tudo:

SELECT col1, col2, col3
FROM
(SELECT * FROM tableA
UNION ALL  
SELECT * FROM tableB) data
GROUP BY col1, col2, col3
HAVING count(*)!=2
jzd
fonte
Tentei usar isso (peguei no blog do SQL Server de Jeff ), mas gostaria de listar as duas linhas da Tabela A e da Tabela B para poder ver visualmente as diferenças nas linhas. Você se importaria de explicar como fazer isso?
Emmanuel F
@Agent, isso parece uma pergunta separada. Eu sugiro listá-lo para que outras pessoas vejam, em vez de apenas um comentário aqui.
jzd de
Feito. E feito. Comparando valores de 2 tabelas e listando as linhas que são diferentes . Espero obter ótimos resultados. :)
Emmanuel F
Isso funciona bem em ambientes SQL limitados, como Visual FoxPro, obrigado!
Kit Roed
1
Apenas revisando isso. Vale ressaltar que as chaves primárias garantem registros únicos nas tabelas. Se uma tabela (ou consulta) puder ter linhas duplicadas, DISTINCT/ GROUP BYé sugerido para as subconsultas na união, para garantir que haja apenas um registro por tabela. Caso contrário, a Tabela A poderia ter 2 registros e a Tabela B poderia ter 0 e não atender à condição HAVING.
vol7ron
8
SELECT c.ID
FROM clients c
WHERE EXISTS(SELECT c2.ID 
FROM clients2 c2
WHERE c2.ID = c.ID);

Retornará todos os IDs que são iguais em ambas as tabelas. Para obter as diferenças, altere EXISTS para NOT EXISTS.

imiz
fonte
3

Pegando o script de um dia quando, eu o modifiquei para mostrar também de qual tabela vem cada entrada.

DECLARE @table1 NVARCHAR(80)= 'table 1 name'
DECLARE @table2 NVARCHAR(80)= 'table 2 name'
DECLARE @sql NVARCHAR (1000)

SET @sql = 
'
SELECT ''' + @table1 + ''' AS table_name,* FROM
(
SELECT * FROM ' + @table1 + '
EXCEPT
SELECT * FROM ' + @table2 + '
) x

UNION 

SELECT ''' + @table2 + ''' AS table_name,* FROM 
(
SELECT * FROM ' + @table2 + '
EXCEPT
SELECT * FROM ' + @table1 + '
) y
'

EXEC sp_executesql @stmt = @sql
Robert Sievers
fonte
2

apenas para completar, um proc armazenado usando o método except para comparar 2 tabelas e dar resultado na mesma tabela com 3 status de erros, ADD, DEL, tabela GAP deve ter o mesmo PK, você declara as 2 tabelas e campos para comparar de 1 ou ambas as tabelas

Basta usar como este ps_TableGap 'tbl1', 'Tbl2', 'fld1, fld2, fld3', 'fld4'fld5'fld6' (opcional)

/****** Object:  StoredProcedure [dbo].[ps_TableGap]    Script Date: 10/03/2013 16:03:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:       Arnaud ALLAVENA
-- Create date: 03.10.2013
-- Description: Compare tables
-- =============================================
create PROCEDURE [dbo].[ps_TableGap]
    -- Add the parameters for the stored procedure here
    @Tbl1 as varchar(100),@Tbl2 as varchar(100),@Fld1 as varchar(1000), @Fld2 as varchar(1000)= ''
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.

    SET NOCOUNT ON;
--Variables
--@Tbl1 = table 1
--@Tbl2 = table 2
--@Fld1 = Fields to compare from table 1
--@Fld2 Fields to compare from table 2
Declare @SQL varchar(8000)= '' --SQL statements
Declare @nLoop int = 1 --loop counter
Declare @Pk varchar(1000)= '' --primary key(s) 
Declare @Pk1 varchar(1000)= '' --first field of primary key
declare @strTmp varchar(50) = '' --returns value in Pk determination
declare @FldTmp varchar (1000) = '' --temporarily fields for alias calculation

--If @Fld2 empty we take @Fld1
--fields rules: fields to be compare must be in same order and type - always returns Gap
If @Fld2 = '' Set @Fld2 = @Fld1

--Change @Fld2 with Alias prefix xxx become _xxx 
while charindex(',',@Fld2)>0
begin
    Set @FldTmp = @FldTmp + (select substring(@Fld2,1,charindex(',',@Fld2)-1) + ' as _' + substring(@Fld2,1,charindex(',',@Fld2)-1) + ',')
    Set @Fld2 = (select ltrim(right(@Fld2,len(@Fld2)-charindex(',',@Fld2))))
end
Set @FldTmp = @FldTmp + @Fld2 + ' as _' + @Fld2
Set @Fld2 = @FldTmp

--Determinate primary key jointure
--rule: same pk in both tables
Set @nLoop = 1
Set @SQL = 'Declare crsr cursor for select COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '''
 + @Tbl1 + ''' or TABLE_SCHEMA + ''.'' + TABLE_NAME = ''' + @Tbl1 +  ''' or TABLE_CATALOG + ''.'' + TABLE_SCHEMA + ''.'' + TABLE_NAME = ''' + @Tbl1 
 + ''' order by ORDINAL_POSITION'
exec(@SQL)
open crsr 
fetch next from crsr into @strTmp
while @@fetch_status = 0
begin 
    if @nLoop = 1 
    begin 
        Set @Pk = 's.' + @strTmp + ' = b._' + @strTmp
        Set @Pk1 = @strTmp
        set @nLoop = @nLoop + 1 
    end 
    Else
    Set @Pk = @Pk + ' and s.' + @strTmp + ' = b._' + @strTmp
fetch next from crsr into @strTmp 

end 
close crsr
deallocate crsr

--SQL statement build
set @SQL = 'select case when s.' + @Pk1 + ' is null then ''Del'' when b._' + @Pk1 + ' is null then ''Add'' else ''Gap'' end as TypErr, '''
set @SQL = @SQL + @Tbl1 +''' as Tbl1, s.*, ''' + @Tbl2 +''' as Tbl2 ,b.* from (Select ' + @Fld1 + ' from ' + @Tbl1
set @SQL = @SQL + ' EXCEPT SELECT ' + @Fld2 + ' from ' + @Tbl2 + ')s full join (Select ' + @Fld2 + ' from ' + @Tbl2 
set @SQL = @SQL + ' EXCEPT SELECT ' + @Fld1 + ' from ' + @Tbl1 +')b on '+ @Pk 

--Run SQL statement
Exec(@SQL)
END
Arnaud ALLAVENA
fonte
2

Fonte: Use NATURAL FULL JOIN para comparar duas tabelas em SQL de Lukas Eder

Abordagem inteligente de uso NATURAL FULL JOINpara detectar as mesmas / diferentes linhas entre duas tabelas.

Exemplo 1 - sinalizador de status:

SELECT t1.*, t2.*, CASE WHEN t1 IS NULL OR t2 IS NULL THEN 'Not equal' ELSE 'Equal' END
FROM t1
NATURAL FULL JOIN t2;

Exemplo 2 - filtragem de linhas

SELECT *
FROM (SELECT 't1' AS t1, t1.* FROM t1) t1 
NATURAL FULL JOIN (SELECT 't2' AS t2, t2.* FROM t2) t2 
WHERE t1 IS NULL OR t2 IS NULL -- show differences
--WHERE  t1 IS NOT NULL AND t2 IS NOT NULL    -- show the same

db <> demonstração de violino

Lukasz Szozda
fonte
1

Aprimoramento da resposta de dietbuddha ...

select * from
(
    select * from tableA
    minus
    select * from tableB
)
union all
select * from
(
    select * from tableB
    minus
    select * from tableA
)
IanMc
fonte
1

Você pode encontrar diferenças de 2 tabelas usando a combinação de inserir tudo e junção externa completa no Oracle. No sql você pode extrair as diferenças via full outer join, mas parece que insert all / first não existe no sql! Portanto, você deve usar a seguinte consulta:

select * from A
full outer join B on
A.pk=B.pk
where A.field1!=B.field1
or A.field2!=B.field2 or A.field3!=B.field3 or A.field4!=B.field4 
--and A.Date==Date1

Embora o uso de 'OR' na cláusula where não seja recomendado e geralmente resulte em desempenho inferior, você ainda pode usar a consulta acima se suas tabelas não forem massivas. Se houver algum resultado para a consulta acima, é exatamente a diferença de 2 tabelas baseadas na comparação dos campos 1,2,3,4. Para melhorar o desempenho da consulta, você também pode filtrá-la por data (verifique a parte comentada)

user3665906
fonte
0
    SELECT unnest(ARRAY[1,2,2,3,3]) 
    EXCEPT
    SELECT unnest(ARRAY[1,1,2,3,3])
UNION
    SELECT unnest(ARRAY[1,1,2,3,3])
    EXCEPT
    SELECT unnest(ARRAY[1,2,2,3,3])

O resultado é nulo, mas as fontes são diferentes!

Mas:

(
    SELECT unnest(ARRAY[1,2,2,3])
    EXCEPT ALL
    SELECT unnest(ARRAY[2,1,2,3])
)
UNION
(
    SELECT unnest(ARRAY[2,1,2,3])
    EXCEPT ALL
    SELECT unnest(ARRAY[1,2,2,3])
)

trabalho.

Kamil Valenta
fonte
0

Eu tive esse mesmo problema no SQL Server e escrevi este script T-SQL para automatizar o processo (na verdade, esta é a versão diluída, o meu escreveu todas as diferenças em uma única tabela para facilitar o relatório).

Atualize 'MyTable' e 'MyOtherTable' para os nomes das tabelas que deseja comparar.

DECLARE @ColName varchar(100)
DECLARE @Table1 varchar(100) = 'MyTable'
DECLARE @Table2 varchar(100) = 'MyOtherTable'


IF (OBJECT_ID('tempdb..#col') IS NOT NULL) DROP TABLE #col
SELECT  IDENTITY(INT, 1, 1) RowNum , c.name
INTO    #col
FROM    SYS.Objects o 
        JOIN SYS.columns c on o.object_id = c.object_id
WHERE   o.name = @Table1 AND NOT c.Name IN ('List','Columns','YouWantToIgnore')

DECLARE @Counter INT = (SELECT MAX(RowNum) FROM #col)

    WHILE @Counter > 0

        BEGIN
            SET @ColName = (SELECT name FROM #Col WHERE RowNum= @Counter)
                EXEC ('SELECT  t1.Identifier
                        ,t1.'+@ColName+' AS '+@Table1+@ColName+'
                        ,t2.'+@ColName+' AS '+@Table2+@ColName+'
                FROM    '+@Table1+' t1
                        LEFT JOIN '+@Table2+' t2 ON t1.Identifier = t2.Identifier 
                WHERE   t1.'+@ColName+' <> t2.'+@ColName)
            SET @Counter = @Counter - 1 
        END
Cyndi Baker
fonte
0

Escrevi isso para comparar os resultados de uma visão bastante desagradável que transferi do Oracle para o SQL Server. Ele cria um par de tabelas temporárias, #DataVariances e #SchemaVariances, com diferenças (você adivinhou) nos dados nas tabelas e no esquema das próprias tabelas.

Requer que ambas as tabelas tenham uma chave primária, mas você pode soltá-la em tempdb com uma coluna de identidade se as tabelas de origem não tiverem uma.

declare @TableA_ThreePartName nvarchar(max) = ''
declare @TableB_ThreePartName nvarchar(max) = ''
declare @KeyName nvarchar(max) = ''

/***********************************************************************************************

    Script to compare two tables and return differneces in schema and data.

    Author: Devin Lamothe       2017-08-11

***********************************************************************************************/
set nocount on

-- Split three part name into database/schema/table
declare @Database_A nvarchar(max) = (
    select  left(@TableA_ThreePartName,charindex('.',@TableA_ThreePartName) - 1))
declare @Table_A nvarchar(max) = (
    select  right(@TableA_ThreePartName,len(@TableA_ThreePartName) - charindex('.',@TableA_ThreePartName,len(@Database_A) + 2)))
declare @Schema_A nvarchar(max) = (
    select  replace(replace(@TableA_ThreePartName,@Database_A + '.',''),'.' + @Table_A,''))

declare @Database_B nvarchar(max) = (
    select  left(@TableB_ThreePartName,charindex('.',@TableB_ThreePartName) - 1))
declare @Table_B nvarchar(max) = (
    select  right(@TableB_ThreePartName,len(@TableB_ThreePartName) - charindex('.',@TableB_ThreePartName,len(@Database_B) + 2)))
declare @Schema_B nvarchar(max) = (
    select  replace(replace(@TableB_ThreePartName,@Database_B + '.',''),'.' + @Table_B,''))

-- Get schema for both tables
declare @GetTableADetails nvarchar(max) = '
    use [' + @Database_A +']
        select  COLUMN_NAME
             ,  DATA_TYPE
          from  INFORMATION_SCHEMA.COLUMNS
         where  TABLE_NAME = ''' + @Table_A + '''
           and  TABLE_SCHEMA = ''' + @Schema_A + '''
    '
create table #Table_A_Details (
    ColumnName nvarchar(max)
,   DataType nvarchar(max)
)
insert into #Table_A_Details
exec (@GetTableADetails)

declare @GetTableBDetails nvarchar(max) = '
    use [' + @Database_B +']
        select  COLUMN_NAME
             ,  DATA_TYPE
          from  INFORMATION_SCHEMA.COLUMNS
         where  TABLE_NAME = ''' + @Table_B + '''
           and  TABLE_SCHEMA = ''' + @Schema_B + '''
    '
create table #Table_B_Details (
    ColumnName nvarchar(max)
,   DataType nvarchar(max)
)
insert into #Table_B_Details
exec (@GetTableBDetails)


-- Get differences in table schema
            select  ROW_NUMBER() over (order by
                        a.ColumnName
                    ,   b.ColumnName) as RowKey
                 ,  a.ColumnName as A_ColumnName
                 ,  a.DataType as A_DataType
                 ,  b.ColumnName as B_ColumnName
                 ,  b.DataType as B_DataType
              into  #FieldList
              from  #Table_A_Details a
   full outer join  #Table_B_Details b
                on  a.ColumnName = b.ColumnName
             where  a.ColumnName is null
                or  b.ColumnName is null
                or  a.DataType <> b.DataType

        drop table  #Table_A_Details
        drop table  #Table_B_Details

            select  coalesce(A_ColumnName,B_ColumnName) as ColumnName
                 ,  A_DataType
                 ,  B_DataType
              into  #SchemaVariances
              from  #FieldList

-- Get differences in table data
declare @LastColumn int = (select max(RowKey) from #FieldList)
declare @RowNumber int = 1
declare @ThisField nvarchar(max)
declare @TestSql nvarchar(max)



create table #DataVariances (
    TableKey            nvarchar(max)
,   FieldName           nvarchar(max)
,   TableA_Value        nvarchar(max)
,   TableB_Value        nvarchar(max)
)

delete from #FieldList where A_DataType in ('varbinary','image') or B_DataType in ('varbinary','image') 

while @RowNumber <= @LastColumn begin
    set @TestSql = '
        select  coalesce(a.[' + @KeyName + '],b.[' + @KeyName + ']) as TableKey
             ,  ''' + @ThisField + ''' as FieldName
             ,  a.[' + @ThisField + '] as [TableA_Value]
             ,  b.[' + @ThisField + '] as [TableB_Value]
          from  [' + @Database_A + '].[' + @Schema_A + '].[' + @Table_A + '] a 
    inner join  [' + @Database_B + '].[' + @Schema_B + '].[' + @Table_B + '] b
            on  a.[' + @KeyName + '] = b.[' + @KeyName + ']
         where  ltrim(rtrim(a.[' + @ThisField + '])) <> ltrim(rtrim(b.[' + @ThisField + ']))
            or (a.[' + @ThisField + '] is null and  b.[' + @ThisField + '] is not null)
            or (a.[' + @ThisField + '] is not null and  b.[' + @ThisField + '] is null)
'

insert into #DataVariances
exec (@TestSql)

set @RowNumber = @RowNumber + 1
set @ThisField = (select coalesce(A_ColumnName,B_ColumnName) from #FieldList a where RowKey = @RowNumber)

end

drop table #FieldList

print 'Query complete.  Select from #DataVariances to verify data integrity or #SchemaVariances to verify schemas match.  Data types varbinary and image are not checked.'
Devin Lamothe
fonte
0

A maioria das respostas parece ignorar a questão levantada por Kamil. (É aí que as tabelas contêm linhas idênticas, mas diferentes são repetidas em cada tabela.) Infelizmente, não estou conseguindo usar a solução dele, porque estou no Oracle. O melhor que consegui sugerir é:

SELECT * FROM
   (
   SELECT column1, column2, ..., COUNT(*) AS the_count
   FROM tableA
   GROUP BY column1, column2, ...
   MINUS
   SELECT column1, column2, ..., COUNT(*) AS the_count
   FROM tableB
   GROUP BY column1, column2, ...
   )
UNION ALL
   (
   SELECT column1, column2, ..., COUNT(*) AS the_count
   FROM tableB
   GROUP BY column1, column2, ...
   MINUS
   SELECT column1, column2, ..., COUNT(*) AS the_count
   FROM tableA
   GROUP BY column1, column2, ...
   )
Mark W. Bolme
fonte
0

Para comparar T1 (PK, A, B) e T2 (PK, A, B).

Primeiro compare os conjuntos de chaves primárias para procurar valores de chave ausentes em qualquer um dos lados:

SELECT T1.*, T2.* FROM T1 FULL OUTER JOIN T2 ON T1.PK=T2.PK WHERE T1.PK IS NULL OR T2.PK IS NULL;

Em seguida, liste todos os valores incompatíveis:

SELECT T1.PK, 'A' AS columnName, T1.A AS leftValue, T2.A AS rightValue FROM T1 JOIN T2 ON T1.PK=T2.PK WHERE COALESCE(T1.A,0) != COALESCE(T2.A,0)
UNION ALL
SELECT T1.PK, 'B' AS columnName, T1.B AS leftValue, T2.B AS rightValue FROM T1 JOIN T2 ON T1.PK=T2.PK WHERE COALESCE(T1.B,0) != COALESCE(T2.B,0)

A e B devem ser do mesmo tipo. Você pode usar o ESQUEMA DE INFORMAÇÕES para gerar o SELECT. Não se esqueça de COALESCE para incluir também os resultados IS NULL. Você também pode usar FULL OUTER JOIN e COALESCE (T1.PK, 0) = COALESCE (T2.PK, 0).

Por exemplo, para colunas do tipo varchar:

SELECT concat('SELECT T1.PK, ''', COLUMN_NAME, ''' AS columnName, T1.', COLUMN_NAME, ' AS leftValue, T2.', COLUMN_NAME, ' AS rightValue FROM T1 JOIN T2 ON T1.PK=T2.PK WHERE COALESCE(T1.',COLUMN_NAME, ',0)!=COALESCE(T2.', COLUMN_NAME, ',0)')
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME='T1' AND DATA_TYPE IN ('nvarchar','varchar');
Ludovic Aubert
fonte
0

Podemos comparar os dados de duas tabelas de tabelas do DB2 usando a consulta simples abaixo,

Etapa 1: - Selecione quais colunas precisamos comparar da tabela (T1) do esquema (S)

     SELECT T1.col1,T1.col3,T1.col5 from S.T1

Passo 2: - Use a palavra-chave 'Menos' para comparar 2 tabelas.

Etapa 3: - Selecione quais colunas precisamos comparar da tabela (T2) do esquema (S)

     SELECT T2.col1,T2.col3,T2.col5 from S.T1

Resultado final:

     SELECT T1.col1,T1.col3,T1.col5 from S.T1
     MINUS 
     SELECT T2.col1,T2.col3,T2.col5 from S.T1;

Se a consulta não retornar nenhuma linha, os dados serão exatamente os mesmos.

Madhushankar MJ
fonte
-1

No MySQL, onde "menos" não é suportado, e levando em consideração o desempenho, este é um

query:
SELECT 
t1.id, 
t1.id 
FROM t1 inner join t2 using (id) where concat(t1.C, t1.D, ...)<>concat(t2.C, t2.D, ...)
Jehad Keriaki
fonte
-1

Uma consulta alternativa e aprimorada com base na resposta de dietbuddha & IanMc. A consulta inclui uma descrição para mostrar de maneira útil onde as linhas existem e estão faltando. (NB: para SQL Server )

(
    select 'InTableA_NoMatchInTableB' as Msg, * from tableA
    except
    select 'InTableA_NoMatchInTableB' , * from tableB
)
union all
(
    select 'InTableB_NoMatchInTableA' as Msg, * from tableB
    except
    select 'InTableB_NNoMatchInTableA' ,* from tableA
)
Terry C
fonte
-1
SELECT * 
FROM TABLE A
WHERE NOT EXISTS (SELECT 'X' 
                  FROM  TABLE B 
                  WHERE B.KEYFIELD1 = A.KEYFIELD1 
                  AND   B.KEYFIELD2 = A.KEYFIELD2 
                  AND   B.KEYFIELD3 = A.KEYFIELD3)
;

'X' é qualquer valor.

Troque as tabelas para ver as diferentes discrepâncias.

Certifique-se de juntar os campos-chave em suas tabelas.

Ou apenas use o operador MINUS com 2 instruções de seleção, no entanto, MINUS só pode funcionar no Oracle.

HEXU55
fonte
menos não é compatível com todas as implementações. (por exemplo, servidor de servidor usa exceto).
Senhor Jura-a-lot