SQL confronta i dati di due tabelle


90

Ho 2 tabelle TableAe TableBche hanno lo stesso formato di colonna, ad esempio entrambe le tabelle TableAe TableBhanno colonne

A B C D E F 

dove A e B sono le chiavi primarie.

Come scrivere SQL per verificare che se TableAe TableBche hanno le stesse chiavi primarie contengano esattamente lo stesso valore in ogni colonna.

Significa che queste due tabelle hanno esattamente gli stessi dati.

Risposte:


82

Dovresti essere in grado di "MINUS" o "EXCEPT" a seconda del tipo di SQL utilizzato dal tuo DBMS.

select * from tableA
minus
select * from tableB

Se la query non restituisce righe, i dati sono esattamente gli stessi.


5
Ottimo suggerimento. Tuttavia, penso che questo potrebbe non funzionare se tableB ha una o più righe in più, quindi potresti voler confrontare anche i conteggi delle righe.
jzd

5
Altrimenti. Non funzionerà se tableAha righe extra. Avresti bisogno (A EXCEPT B) INTERSECT (B EXCEPT A)immagino che questo sarebbe molto meno efficiente di un join standard di palude.
Martin Smith

la query restituisce due set di risultati ??
BuZz

Questa query restituirà righe con NULL se presenti.
Reeya Oberoi

6
@Franklin - No, dovrebbe restituire solo un gruppo di risultati. Se ne ottieni due, usa EXCEPT al posto di MENO.
MTS

57

Utilizzo di operatori relazionali:

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

Cambia EXCEPTin MINUSper Oracle.

Punto leggermente esigente: quanto sopra si basa sulla precedenza degli operatori, che secondo lo standard SQL dipende dall'implementazione, quindi YMMV. Funziona per SQL Server, per il quale la precedenza è:

  1. Espressioni tra parentesi
  2. INTERSECT
  3. EXCEPTe UNIONvalutato da sinistra a destra.

Per Oracle, è necessario utilizzare parentesi attorno a UNION, parentesi attorno a INTERSECT e (come indicato) sostituire EXCEPT con MINUS. HTH.
Doug Clutter

20

dietbuddha ha una bella risposta. Nei casi in cui non hai un MINUS o EXCEPT, un'opzione è fare un'unione di tutte le tabelle, raggruppare con tutte le colonne e assicurarti che ci siano due di tutto:

SELECT col1, col2, col3
FROM
(SELECT * FROM tableA
UNION ALL  
SELECT * FROM tableB) data
GROUP BY col1, col2, col3
HAVING count(*)!=2

Ho provato a usarlo (l'ho preso dal blog di Jeff su SQL Server ) ma vorrei elencare entrambe le righe da TableA e TableB in modo da poter vedere visivamente le differenze nelle righe. Ti dispiacerebbe spiegare come farlo?
Emmanuel F

@Agente, questa suona come una domanda a parte. Suggerirei di elencarlo in modo che altri lo vedano, piuttosto che un semplice commento qui.
jzd

Fatto. E fatto. Confronto dei valori di 2 tabelle e elenco delle righe diverse . Spero di ottenere ottimi risultati. :)
Emmanuel F

Funziona bene in ambienti SQL limitati come Visual FoxPro, grazie!
Kit Roed

1
Sto solo rivedendo questo. Vale la pena ricordare che le chiavi primarie garantiscono record univoci nelle tabelle. Se una tabella (o una query) può avere righe duplicate, viene suggerito DISTINCT/ GROUP BYper le sottoquery nell'unione, per garantire che ci sia un solo record per tabella. In caso contrario, TableA potrebbe avere 2 record e TableB potrebbe avere 0 e non soddisfare la condizione HAVING.
vol7ron

8
SELECT c.ID
FROM clients c
WHERE EXISTS(SELECT c2.ID 
FROM clients2 c2
WHERE c2.ID = c.ID);

Restituirà tutti gli ID identici in entrambe le tabelle. Per ottenere le differenze, modificare EXISTS in NOT EXISTS.


3

Prendendo la sceneggiatura di onedaywhen, l'ho modificata per mostrare anche da quale tabella proviene ciascuna voce.

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

2

solo per completare, un processo memorizzato utilizzando il metodo tranne per confrontare 2 tabelle e dare il risultato nella stessa tabella con 3 stati di errore, ADD, DEL, tabella GAP deve avere lo stesso PK, dichiari le 2 tabelle e campi da confrontare di 1 o di entrambe le tabelle

Basta usare come questo ps_TableGap 'tbl1', 'Tbl2', 'fld1, fld2, fld3', 'fld4'fld5'fld6' (opzionale)

/****** 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

2

Fonte: utilizzare NATURAL FULL JOIN per confrontare due tabelle in SQL di Lukas Eder

Approccio intelligente di utilizzo NATURAL FULL JOINper rilevare le stesse / diverse righe tra due tabelle.

Esempio 1 - flag di stato:

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

Esempio 2: filtraggio delle righe

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 <> fiddle demo


1

Miglioramento alla risposta di dietbuddha ...

select * from
(
    select * from tableA
    minus
    select * from tableB
)
union all
select * from
(
    select * from tableB
    minus
    select * from tableA
)

1

È possibile trovare differenze di 2 tabelle utilizzando la combinazione di insert all e full outer join in Oracle. In sql puoi estrarre le differenze tramite full outer join ma sembra che inserisci tutto / primo non esista in sql! Quindi, devi usare invece la seguente query:

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

Sebbene l'utilizzo di "OR" nella clausola where non sia consigliato e di solito produce prestazioni inferiori, è comunque possibile utilizzare la query sopra se le tabelle non sono enormi. Se c'è un risultato per la query sopra, sono esattamente le differenze di 2 tabelle basate sul confronto dei campi 1,2,3,4. Per migliorare le prestazioni della query, puoi anche filtrarla per data (controlla la parte commentata)


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])

Il risultato è nullo, ma le fonti sono diverse!

Ma:

(
    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])
)

lavori.


0

Ho avuto lo stesso problema in SQL Server e ho scritto questo script T-SQL per automatizzare il processo (in realtà questa è la versione annacquata, la mia ha scritto tutte le differenze su una singola tabella per un facile reporting).

Aggiorna "MyTable" e "MyOtherTable" con i nomi delle tabelle che desideri confrontare.

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

0

Ho scritto questo per confrontare i risultati di una vista piuttosto sgradevole che ho portato da Oracle a SQL Server. Crea una coppia di tabelle temporanee, #DataVariances e #SchemaVariances, con differenze (hai indovinato) nei dati nelle tabelle e nello schema delle tabelle stesse.

Richiede che entrambe le tabelle abbiano una chiave primaria, ma potresti rilasciarla in tempdb con una colonna Identity se le tabelle di origine non ne hanno una.

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.'

0

La maggior parte delle risposte sembra ignorare la questione sollevata da Kamil. (È qui che le tabelle contengono righe identiche, ma diverse vengono ripetute in ciascuna tabella.) Sfortunatamente, non sono in grado di utilizzare la sua soluzione, perché sono in Oracle. Il meglio che sono riuscito a trovare è:

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, ...
   )

0

Per confrontare T1 (PK, A, B) e T2 (PK, A, B).

Prima confronta i set di chiavi primarie per cercare i valori delle chiavi mancanti su entrambi i lati:

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

Quindi elenca tutti i valori non corrispondenti:

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 devono essere dello stesso tipo. È possibile utilizzare lo SCHEMA DELLE INFORMAZIONI per generare il file SELECT. Non dimenticare il COALESCE per includere anche i risultati IS NULL. È inoltre possibile utilizzare FULL OUTER JOIN e COALESCE (T1.PK, 0) = COALESCE (T2.PK, 0).

Ad esempio per colonne di 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');

0

Possiamo confrontare i dati di due tabelle di tabelle DB2 utilizzando la seguente semplice query,

Passaggio 1: - Seleziona quali colonne dobbiamo confrontare dalla tabella (T1) dello schema (S)

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

Passaggio 2: - Utilizza la parola chiave "Meno" per confrontare 2 tabelle.

Passaggio 3: - Seleziona quali colonne dobbiamo confrontare dalla tabella (T2) dello schema (S)

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

Risultato finale:

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

Se la query non restituisce righe, i dati sono esattamente gli stessi.


-1

In MySQL, dove "meno" non è supportato e tenendo conto delle prestazioni, questo è un veloce

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

-1

Una query alternativa e migliorata basata sulla risposta di dietbuddha e IanMc. La query include una descrizione per mostrare in modo utile dove esistono e mancano le righe. (NB: per 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
)

-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" è un valore qualsiasi.

Cambia le tabelle per vedere le diverse discrepanze.

Assicurati di unire i campi chiave nelle tue tabelle.

O semplicemente usa l'operatore MINUS con 2 istruzioni select, tuttavia, MINUS può funzionare solo in Oracle.


meno non è supportato in tutte le implementazioni. (es. server server utilizza tranne).
Sir Swears-a-lot
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.