SQL compare les données de deux tables

88

J'ai 2 tables TableAet TableBqui ont le même format de colonne par exemple les deux tables TableAet TableBont des colonnes

A B C D E F 

où A et B sont les clés primaires.

Comment écrire SQL pour vérifier que si TableAet TableBqui ont les mêmes clés primaires contiennent exactement la même valeur dans toutes les colonnes.

Cela signifie que ces deux tables contiennent exactement les mêmes données.

Nikky
la source

Réponses:

81

Vous devriez pouvoir "MOINS" ou "SAUF" selon la saveur de SQL utilisée par votre SGBD.

select * from tableA
minus
select * from tableB

Si la requête ne renvoie aucune ligne, les données sont exactement les mêmes.

régime bouddha
la source
5
Excellente suggestion. Cependant, je pense que cela pourrait ne pas fonctionner si tableB a une ou plusieurs lignes supplémentaires, vous pouvez donc comparer le nombre de lignes en plus.
jzd
5
Dans l'autre sens. Cela ne fonctionnera pas s'il y tableAa des lignes supplémentaires. Vous auriez besoin, (A EXCEPT B) INTERSECT (B EXCEPT A)je suppose que ce serait beaucoup moins efficace qu'une jointure standard de tourbière.
Martin Smith
la requête renvoie deux jeux de résultats ??
BuZz
Cette requête renverra des lignes avec NULLS le cas échéant.
Reeya Oberoi
5
@Franklin - Non, il ne devrait renvoyer qu'un seul jeu de résultats. Si vous en obtenez deux, utilisez EXCEPT à la place de MOINS.
MTS
56

Utilisation des opérateurs relationnels:

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

Changer EXCEPTà MINUSpour Oracle.

Point légèrement difficile: ce qui précède repose sur la priorité des opérateurs, qui, selon la norme SQL, dépend de l'implémentation, donc YMMV. Cela fonctionne pour SQL Server, pour lequel la priorité est:

  1. Expressions entre parenthèses
  2. INTERSECT
  3. EXCEPTet UNIONévalué de gauche à droite.
un jour quand
la source
Pour Oracle, vous devez utiliser des parenthèses autour de l'UNION, des parenthèses autour de l'INTERSECT et (comme indiqué) remplacer EXCEPT par MOINS. HTH.
Doug Clutter
20

dietbuddha a une bonne réponse. Dans les cas où vous n'avez pas de MOINS ou D'EXCEPTION, une option est de faire une union entre les tables, regrouper avec toutes les colonnes et assurez-vous qu'il y en a deux de tout:

SELECT col1, col2, col3
FROM
(SELECT * FROM tableA
UNION ALL  
SELECT * FROM tableB) data
GROUP BY col1, col2, col3
HAVING count(*)!=2
jzd
la source
J'ai essayé de l'utiliser (je l'ai obtenu sur le blog SQL Server de Jeff ) mais je voudrais lister les deux lignes de TableA et TableB afin que je puisse voir visuellement les différences dans les lignes. Pourriez-vous expliquer comment faire cela?
Emmanuel F
@Agent, cela ressemble à une question distincte. Je suggérerais de l'énumérer pour que les autres le voient, plutôt qu'un simple commentaire ici.
jzd
Terminé. Et.. Voila. Comparer les valeurs de 2 tableaux et lister les lignes qui sont différentes . J'espère que j'obtiendrai d'excellents résultats. :)
Emmanuel F
Cela fonctionne bien dans les environnements SQL limités comme Visual FoxPro, merci!
Kit Roed
1
Je viens de revoir cela. Il est à noter que les clés primaires garantissent des enregistrements uniques dans les tables. Que si une table (ou une requête) peut avoir des lignes en double, DISTINCT/ GROUP BYest suggéré pour les sous-requêtes dans l'union, pour s'assurer qu'il n'y a qu'un seul enregistrement par table. Sinon, TableA pourrait avoir 2 enregistrements et TableB pourrait avoir 0 et ne pas remplir la condition HAVING.
vol7ron
8
SELECT c.ID
FROM clients c
WHERE EXISTS(SELECT c2.ID 
FROM clients2 c2
WHERE c2.ID = c.ID);

Renvoie tous les ID qui sont les MÊMES dans les deux tables. Pour obtenir les différences, remplacez EXISTS par NOT EXISTS.

imiz
la source
3

Prenant le script à partir d'un jour, je l'ai modifié pour montrer également de quelle table provient chaque entrée.

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
la source
2

juste pour terminer, un processus stocké en utilisant la méthode except pour comparer 2 tables et donner le résultat dans la même table avec 3 états d'erreurs, ADD, DEL, GAP table doit avoir le même PK, vous déclarez les 2 tables et champs à comparer de 1 ou des deux

Utilisez simplement comme ceci ps_TableGap 'tbl1', 'Tbl2', 'fld1, fld2, fld3', 'fld4'fld5'fld6' (facultatif)

/****** 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
la source
2

Source: Utilisez NATURAL FULL JOIN pour comparer deux tables en SQL par Lukas Eder

Approche intelligente d'utilisation NATURAL FULL JOINpour détecter les mêmes / différentes lignes entre deux tables.

Exemple 1 - indicateur d'état:

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

Exemple 2 - Filtrage des lignes

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

démo db <> fiddle

Lukasz Szozda
la source
1

Amélioration de la réponse de Dietbuddha ...

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

Vous pouvez trouver les différences de 2 tables en utilisant la combinaison de insérer tout et de jointure externe complète dans Oracle. Dans SQL, vous pouvez extraire les différences via une jointure externe complète, mais il semble que insérer tout / d'abord n'existe pas dans SQL! Par conséquent, vous devez utiliser la requête suivante à la place:

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

Bien que l'utilisation de la clause 'OR' dans la clause where ne soit pas recommandée et qu'elle génère généralement des performances inférieures, vous pouvez toujours utiliser la requête ci-dessus si vos tables ne sont pas massives. S'il y a un résultat pour la requête ci-dessus, ce sont exactement les différences de 2 tables basées sur la comparaison des champs 1,2,3,4. Pour améliorer les performances de la requête, vous pouvez également la filtrer par date (vérifiez la partie commentée)

user3665906
la source
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])

Le résultat est nul, mais les sources sont différentes!

Mais:

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

travaux.

Kamil Valenta
la source
0

J'ai eu ce même problème dans SQL Server et j'ai écrit ce script T-SQL pour automatiser le processus (en fait, c'est la version édulcorée, la mienne a écrit tous les diff dans une seule table pour faciliter les rapports).

Mettez à jour «MyTable» et «MyOtherTable» avec les noms des tables que vous souhaitez comparer.

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
la source
0

J'ai écrit ceci pour comparer les résultats d'une vue assez désagréable que j'ai portée d'Oracle vers SQL Server. Il crée une paire de tables temporaires, #DataVariances et #SchemaVariances, avec des différences dans (vous l'avez deviné) les données dans les tables et le schéma des tables elles-mêmes.

Il faut que les deux tables aient une clé primaire, mais vous pouvez la déposer dans tempdb avec une colonne d'identité si les tables source n'en ont pas.

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
la source
0

La plupart des réponses semblent ignorer la question soulevée par Kamil. (C'est là que les tables contiennent des lignes identiques, mais des lignes différentes sont répétées dans chaque table.) Malheureusement, je ne peux pas utiliser sa solution, car je suis dans Oracle. Le mieux que j'ai pu trouver est:

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
la source
0

Pour comparer T1 (PK, A, B) et T2 (PK, A, B).

Commencez par comparer les ensembles de clés primaires pour rechercher les valeurs de clé manquantes de chaque côté:

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

Ensuite, listez toutes les incohérences de valeurs:

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 et B doivent être du même type. Vous pouvez utiliser INFORMATION SCHEMA pour générer le SELECT. N'oubliez pas le COALESCE pour inclure également les résultats IS NULL. Vous pouvez également utiliser FULL OUTER JOIN et COALESCE (T1.PK, 0) = COALESCE (T2.PK, 0).

Par exemple pour les colonnes de type 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
la source
0

Nous pouvons comparer les données de deux tables de tables DB2 à l'aide de la requête simple ci-dessous,

Étape 1: - Sélectionnez toutes les colonnes à comparer à partir du tableau (T1) du schéma (S)

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

Étape 2: - Utilisez le mot-clé «Moins» pour comparer 2 tableaux.

Étape 3: - Sélectionnez toutes les colonnes à comparer à partir du tableau (T2) du schéma (S)

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

Résultat FIN:

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

Si la requête ne renvoie aucune ligne, les données sont exactement les mêmes.

Madhushankar MJ
la source
-1

Dans MySQL, où "moins" n'est pas pris en charge et compte tenu des performances, il s'agit d'un rapide

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
la source
-1

Une requête alternative et améliorée basée sur la réponse de dietbuddha & IanMc. La requête comprend une description pour montrer utilement où les lignes existent et sont manquantes. (NB: pour 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
la source
-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» est une valeur quelconque.

Changez de tableau pour voir les différents écarts.

Assurez-vous de joindre les champs clés de vos tables.

Ou utilisez simplement l'opérateur MINUS avec 2 instructions select, cependant, MINUS ne peut fonctionner que dans Oracle.

HEXU55
la source
minus n'est pas pris en charge dans toutes les implémentations. (par exemple, le serveur utilise sauf).
Sir Swears-a-lot