web-dev-qa-db-ger.com

SQL-Abfrage, um Unterschiede zwischen zwei Tabellen zurückzugeben

Ich versuche, zwei Tabellen, SQL Server, zu vergleichen, um einige Daten zu überprüfen. Ich möchte alle Zeilen aus beiden Tabellen zurückgeben, in denen sich die Daten entweder in der einen oder in der anderen befinden. Im Wesentlichen möchte ich alle Diskrepanzen zeigen. Ich muss dabei drei Daten überprüfen: Vorname, Nachname und Produkt.

Ich bin ziemlich neu in SQL und es scheint, als ob viele der Lösungen, die ich finde, die Dinge komplizierter machen. Ich muss mir keine Sorgen um NULL machen.

Ich habe damit angefangen, so etwas zu versuchen:

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

Ich habe jedoch Probleme, das weiterzuentwickeln.

Vielen Dank!

BEARBEITEN:

Basierend auf der Antwort von @treaschf habe ich versucht, eine Variante der folgenden Abfrage zu verwenden:

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)

Aber ich bekomme immer wieder 0 Ergebnisse zurück, wenn ich weiß, dass mindestens 1 Zeile in td ist, die nicht in d ist.

BEARBEITEN:

Ok, ich denke, ich habe es herausgefunden. Zumindest in meinen wenigen Minuten des Testens scheint es gut genug zu funktionieren.

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

Dies wird mir im Grunde sagen, was in meinen Testdaten ist, das ist nicht in meinen realen Daten. Welches ist völlig in Ordnung für das, was ich tun muss.

173
Casey

WENN Sie Tabellen A und B haben, beide mit colum C, sind hier die Datensätze, die in Tabelle A, aber nicht in B:

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

Um alle Unterschiede mit einer einzelnen Abfrage zu ermitteln, muss ein vollständiger Join wie folgt verwendet werden:

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

In diesem Fall müssen Sie wissen, dass, wenn ein Datensatz in A, aber nicht in B gefunden wird, die Spalten, die aus B stammen, NULL sind und in ähnlicher Weise sind für diejenigen, die in B und nicht in A vorhanden sind, die Spalten von A null.

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

Ich weiß, dass dies möglicherweise keine beliebte Antwort ist, aber ich stimme mit @Randy Minder darin überein, Tools von Drittanbietern zu verwenden, wenn ein komplexerer Vergleich erforderlich ist.

Dieser spezielle Fall ist hier einfach und für diesen Fall werden solche Tools nicht benötigt, aber dies kann leicht komplex werden, wenn Sie mehr Spalten, Datenbanken auf zwei Servern, komplexere Vergleichskriterien usw. einführen.

Es gibt viele dieser Tools wie ApexSQL Data Diff oder Quest Toad und Sie können sie immer im Testmodus verwenden, um die Arbeit zu erledigen.

36
Maisie John

Um alle Unterschiede zwischen zwei Tabellen zu erhalten, können Sie wie ich diese SQL-Anfrage verwenden:

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

Einfache Variation der @ erikkallen-Antwort, die anzeigt, in welcher Tabelle sich die Zeile befindet:

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

Wenn Sie einen Fehler bekommen

Alle Abfragen, die mit einem UNION-, INTERSECT- oder EXCEPT-Operator kombiniert werden, müssen die gleiche Anzahl von Ausdrücken in ihren Ziellisten enthalten.

dann kann es helfen, hinzuzufügen

(   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

Wenn Sie wissen möchten, welche Spaltenwerte unterschiedlich sind, können Sie das Entity-Attribute-Value-Modell verwenden:

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
)

SQL FIDDLE BEISPIEL

4
Roman Pekar

Dies erledigt den Trick, ähnlich wie bei der Lösung von Tiago , und gibt auch die Tabelle "source" zurück.

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

das Ergebnis enthält Unterschiede zwischen den Tabellen. In der Spalte _tabloc befindet sich ein Tabellenverweis.

Versuche dies :

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

Viel einfacher zu lesen.

2
Kango_V

Für einen einfachen Rauchtest, bei dem Sie sicherstellen möchten, dass zwei Tabellen übereinstimmen, ohne sich Gedanken über die Spaltennamen zu machen:

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

Sie können einfach eine Speicherprozedur schreiben, um einen Stapel von Tabellen zu vergleichen.

1
thomas398

Es gibt ein Leistungsproblem im Zusammenhang mit der linken Verknüpfung sowie der vollständigen Verknüpfung mit großen Datenmengen.

Meiner Meinung nach ist dies die beste Lösung:

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

Ich komme aus der Zukunft, die Sie jetzt verwenden können, außer zum Beispiel so etwas:

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