web-dev-qa-db-fra.com

requête SQL pour renvoyer les différences entre deux tables

J'essaie de comparer deux tables, SQL Server, pour vérifier certaines données. Je veux renvoyer toutes les lignes des deux tables où les données se trouvent dans l'une ou l'autre. En substance, je veux montrer toutes les divergences. Pour ce faire, j'ai besoin de vérifier trois données: Prénom, Nom et Produit.

Je suis assez nouveau en SQL et il semble que bon nombre des solutions que je trouve compliquent les choses à outrance. Je n'ai pas à me soucier des NULL.

J'ai commencé par essayer quelque chose comme ceci:

SELECT DISTINCT [First Name], [Last Name], [Product Name] FROM [Temp Test Data]
WHERE ([First Name] NOT IN (SELECT [First Name] 
FROM [Real Data]))

J'ai du mal à aller plus loin cependant.

Merci!

MODIFIER:

D'après la réponse de @treaschf, j'ai essayé d'utiliser une variante de la requête suivante:

SELECT td.[First Name], td.[Last Name], td.[Product Name]
FROM [Temp Test Data] td FULL OUTER JOIN [Data] AS d 
ON td.[First Name] = d.[First Name] AND td.[Last Name] = d.[Last Name] 
WHERE (d.[First Name] = NULL) AND (d.[Last Name] = NULL)

Mais j'obtiens toujours 0 résultat, quand je sais qu'il y a au moins 1 ligne dans td qui n'est pas en d.

MODIFIER:

Ok, je pense que je l'ai compris. Au moins dans mes quelques minutes d’essais, cela semble fonctionner assez bien.

SELECT [First Name], [Last Name]
FROM [Temp Test Data] AS td
WHERE (NOT EXISTS
        (SELECT [First Name], [Last Name]
         FROM [Data] AS d
         WHERE ([First Name] = td.[First Name]) OR ([Last Name] = td.[Last Name])))

Ceci va fondamentalement me dire ce qui est dans mes données de test qui est pas dans mes données réelles. Ce qui convient parfaitement à ce que je dois faire.

173
Casey

SI vous avez des tables A et B, toutes deux avec colum C, voici les enregistrements présents dans la table A mais pas dans B:

SELECT A.*
FROM A
    LEFT JOIN B ON (A.C = B.C)
WHERE B.C IS NULL

Pour obtenir toutes les différences avec une seule requête, une jointure complète doit être utilisée, comme ceci:

SELECT A.*, B.*
FROM A
    FULL JOIN B ON (A.C = B.C)
WHERE A.C IS NULL OR B.C IS NULL

Ce que vous devez savoir dans ce cas, c’est que, lorsqu'un enregistrement peut être trouvé dans A, mais pas dans B, les colonnes qui proviennent de B seront NULL, et de même pour ceux qui sont présents dans B et non dans A, les colonnes de A seront nulles.

188
treaschf
(   SELECT * FROM table1
    EXCEPT
    SELECT * FROM table2)  
UNION ALL
(   SELECT * FROM table2
    EXCEPT
    SELECT * FROM table1) 
228
erikkallen

Je sais que ce n'est peut-être pas une réponse populaire, mais je suis d'accord avec @Randy Minder pour utiliser un outil tiers lorsque des comparaisons plus complexes sont nécessaires.

Ce cas spécifique est facile ici et dans ce cas, de tels outils ne sont pas nécessaires, mais cela peut devenir complexe si vous introduisez plus de colonnes, des bases de données sur deux serveurs, des critères de comparaison plus complexes, etc.

Il existe un grand nombre de ces outils tels que ApexSQL Data Diff ou Quest Toad et vous pouvez toujours les utiliser en mode d'évaluation pour effectuer le travail.

36
Maisie John

Pour obtenir toutes les différences entre deux tables, vous pouvez utiliser comme moi cette requête SQL:

SELECT 'TABLE1-ONLY' AS SRC, T1.*
FROM (
      SELECT * FROM Table1
      EXCEPT
      SELECT * FROM Table2
      ) AS T1
UNION ALL
SELECT 'TABLE2-ONLY' AS SRC, T2.*
FROM (
      SELECT * FROM Table2
      EXCEPT
      SELECT * FROM Table1
      ) AS T2
;
12
bilelovitch

Variation simple sur la réponse @erikkallen indiquant la table dans laquelle la ligne est présente:

(   SELECT 'table1' as source, * FROM table1
    EXCEPT
    SELECT * FROM table2)  
UNION ALL
(   SELECT 'table2' as source, * FROM table2
    EXCEPT
    SELECT * FROM table1) 

Si vous obtenez une erreur

Toutes les requêtes combinées à l'aide d'un opérateur UNION, INTERSECT ou EXCEPT doivent avoir un nombre égal d'expressions dans leurs listes de cibles.

alors il peut être utile d'ajouter

(   SELECT 'table1' as source, * FROM table1
    EXCEPT
    SELECT 'table1' as source, * FROM table2)  
UNION ALL
(   SELECT 'table2' as source, * FROM table2
    EXCEPT
    SELECT 'table2' as source, * FROM table1) 
5
studgeek

Si vous voulez savoir quelles valeurs de colonne sont différentes, vous pouvez utiliser le modèle Entity-Attribute-Value:

declare @Data1 xml, @Data2 xml

select @Data1 = 
(
    select * 
    from (select * from Test1 except select * from Test2) as a
    for xml raw('Data')
)

select @Data2 = 
(
    select * 
    from (select * from Test2 except select * from Test1) as a
    for xml raw('Data')
)

;with CTE1 as (
    select
        T.C.value('../@ID', 'bigint') as ID,
        T.C.value('local-name(.)', 'nvarchar(128)') as Name,
        T.C.value('.', 'nvarchar(max)') as Value
    from @Data1.nodes('Data/@*') as T(C)    
), CTE2 as (
    select
        T.C.value('../@ID', 'bigint') as ID,
        T.C.value('local-name(.)', 'nvarchar(128)') as Name,
        T.C.value('.', 'nvarchar(max)') as Value
    from @Data2.nodes('Data/@*') as T(C)     
)
select
    isnull(C1.ID, C2.ID) as ID, isnull(C1.Name, C2.Name) as Name, C1.Value as Value1, C2.Value as Value2
from CTE1 as C1
    full outer join CTE2 as C2 on C2.ID = C1.ID and C2.Name = C1.Name
where
not
(
    C1.Value is null and C2.Value is null or
    C1.Value is not null and C2.Value is not null and C1.Value = C2.Value
)

EXEMPLE DE FIDDLE SQL

4
Roman Pekar

Cela fera l'affaire, similaire avec la solution de Tiago , renvoie également le tableau "source".

select [First name], [Last name], max(_tabloc) as _tabloc
from (
  select [First Name], [Last name], 't1' as _tabloc from table1
  union all
  select [First name], [Last name], 't2' as _tabloc from table2
) v
group by [Fist Name], [Last name]
having count(1)=1

le résultat contiendra des différences entre les tables. Dans la colonne _tabloc, vous aurez une référence à la table.

2

Essaye ça :

SELECT 
    [First Name], [Last Name]
FROM 
    [Temp Test Data] AS td EXCEPTION JOIN [Data] AS d ON 
         (d.[First Name] = td.[First Name] OR d.[Last Name] = td.[Last Name])

Beaucoup plus simple à lire.

2
Kango_V

Pour un test de fumée simple où vous essayez de vous assurer que deux tables correspondent sans vous soucier des noms de colonne:

--ensure tables have matching records
Select count (*) from tbl_A
Select count (*) from tbl_B

--create temp table of all records in both tables
Select * into #demo from tbl_A 
Union All
Select * from tbl_B

--Distinct #demo records = Total #demo records/2 = Total tbl_A records = total tbl_B records
Select distinct * from #demo 

Vous pouvez facilement écrire une procédure de magasin pour comparer un lot de tables.

1
thomas398

Il existe un problème de performances lié à la jointure gauche ainsi qu’à la jointure complète avec des données volumineuses.

A mon avis c'est la meilleure solution:

select [First Name], count(1) e from (select * from [Temp Test Data] union all select * from [Temp Test Data 2]) a group by [First Name] having e = 1
0
Tiago Moutinho

Je viens de l'avenir maintenant, vous pouvez utiliser sauf, par exemple, quelque chose comme ça:

-- DB1..Tb1 have values than DB2..Tb1 not have
Select Col1,Col2,Col3 From DB1..Tb1
except
Select Col1,Col2,Col3 From DB2..Tb1
-- Now we change order
-- DB2..Tb1 have values than DB1..Tb1 not have
Select Col1,Col2,Col3 From DB2..Tb1
except
Select Col1,Col2,Col3 From DB1..Tb1
0
GiovaniSalazar