SQL-запрос для возврата различий между двумя таблицами - PullRequest
163 голосов
/ 16 января 2010

Я пытаюсь сравнить две таблицы SQL Server, чтобы проверить некоторые данные. Я хочу вернуть все строки из обеих таблиц, где данные находятся в одной или другой. По сути, я хочу показать все несоответствия. При этом мне нужно проверить три фрагмента данных: FirstName, LastName и Product.

Я довольно новичок в SQL, и кажется, что многие решения, которые я нахожу, связаны со сложными вещами. Мне не нужно беспокоиться о NULL.

Я начал с того, что попробовал что-то вроде этого:

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

У меня проблемы с дальнейшим продвижением.

Спасибо!

EDIT:

Основываясь на ответе @treaschf, я пытался использовать вариант следующего запроса:

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)

Но я продолжаю получать 0 результатов назад, когда я знаю, что есть по крайней мере 1 строка в td, которой нет в d.

EDIT:

Хорошо, думаю, я понял это. По крайней мере, за несколько минут тестирования он, кажется, работает достаточно хорошо.

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

Это в основном скажет мне, что находится в моих тестовых данных, а не в моих реальных данных. Что вполне подходит для того, что мне нужно сделать.

Ответы [ 10 ]

218 голосов
/ 16 января 2010
(   SELECT * FROM table1
    EXCEPT
    SELECT * FROM table2)  
UNION ALL
(   SELECT * FROM table2
    EXCEPT
    SELECT * FROM table1) 
183 голосов
/ 16 января 2010

Если у вас есть таблицы A и B, оба с столбцом C, вот записи, которые присутствуют в таблице A, но отсутствуют в B:

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

Чтобы получить все различия одним запросом, необходимо использовать полное объединение, например:

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

Что вам нужно знать в этом случае, так это то, что когда запись может быть найдена в A, но не в B, столбцы из B будут иметь значение NULL, и аналогично которые присутствуют в B, а не в A, столбцы из A будут нулевыми.

36 голосов
/ 14 мая 2013

Я знаю, что это может быть не очень популярный ответ, но я согласен с @Randy Minder в использовании стороннего инструмента, когда требуется более сложное сравнение.

Этот конкретный случай здесь прост, и в этом случае такие инструменты не нужны, но он может легко стать сложным, если вы введете больше столбцов, баз данных на двух серверах, более сложные критерии сравнения и так далее.

Существует много таких инструментов, как ApexSQL Data Diff или Quest Toad , и вы всегда можете использовать их в пробном режиме, чтобы выполнить свою работу.

9 голосов
/ 07 декабря 2015

Чтобы получить все различия между двумя таблицами, вы можете использовать, как и я, этот 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
;
4 голосов
/ 26 июля 2013

Если вы хотите узнать, какие значения столбцов отличаются, вы можете использовать модель 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
)

ПРИМЕР SQL FIDDLE

3 голосов
/ 14 декабря 2016

Простой вариант ответа @erikkallen, показывающий, в какой таблице находится строка:

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

Если вы получили ошибку

Все запросы, объединенные с использованием оператора UNION, INTERSECT или EXCEPT, должны иметь одинаковое количество выражений в своих целевых списках.

тогда это может помочь добавить

(   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) 
2 голосов
/ 02 ноября 2014

Это сделает то же самое, что и решение Tiago , также вернет таблицу «источник»

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

результат будет содержать различия между таблицами, в столбце _tabloc будет указана ссылка на таблицу.

2 голосов
/ 16 января 2010

Попробуйте это:

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

Гораздо проще читать.

1 голос
/ 22 ноября 2013

Для простого теста дыма, в котором вы пытаетесь убедиться, что две таблицы совпадают, не беспокоясь о названиях столбцов:

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

Вы можете легко написать процедуру хранения для сравнения пакета таблиц.

0 голосов
/ 10 сентября 2014

Существует проблема производительности, связанная с левым соединением, а также полным соединением с большими данными.

На мой взгляд, это лучшее решение:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...