2014-11-25 2 views
1

Я хочу найти новые, измененные и удаленные записи в одной таблице (tableA), сравнив ее с другой таблицей (tableB). Обе таблицы имеют одну и ту же схему и имеют уникальное поле идентификатора.Найти измененные/новые/удаленные записи между двумя таблицами

В моей ситуации tableA первоначально совпадает с таблицей B, но он был отредактирован какой-либо внешней организацией, и как только они сделали свои изменения, они отправляют таблицу обратно через ZIP-файл, и мы повторно заполняем (усекаем и вставляем), что данные на tableA. Поэтому я хочу узнать, какие записи изменились в tableA. Я использую SQL Server 2012.

я могу получить новые и измененные записи с «исключением» ключевое слово:

select * from tableA 
except 
select * form tableB 

(Давайте называть полученные результаты ResultsA)

можно также получить удаленные и модифицированные записей:

select * from tableB 
except 
select * form tableA 

(Давайте называть полученные результаты ResultsB)

Проблема заключается в том, что как ResultsA, так и ResultsB имеют те же записи, которые были изменены/отредактированы. Таким образом, измененные/отредактированные записи удваиваются. Я могу использовать внутреннее соединение или пересекаться на ResultsA и ResultsB, чтобы получить только измененные записи (назовите это results ResultsC). Но тогда мне нужно будет использовать join/except снова между ResultsA и ResultsC, чтобы получить только новые записи и снова присоединить/исключить между результатами B и ResultsC, чтобы получить только удаленные записей ... Я пробовал this и this, но они не работают для меня.

Очевидно, что это не хорошо. Есть ли какие-либо изящные и более простые способы узнать записи, которые были удалены, изменены или добавлены в tableA по сравнению с tableB?

ответ

2

Как насчет:

-- DELETED 
SELECT B.*, 'DELETED' AS 'CHANGE_TYPE' 
FROM TableB B 
LEFT JOIN TableA A ON B.PK_ID = A.PK_ID 
WHERE A.PK_ID IS NULL 
UNION 
-- NEW 
SELECT A.*, 'NEW' AS 'CHANGE_TYPE' 
FROM TableA A 
LEFT JOIN TableB B ON B.PK_ID = A.PK_ID 
WHERE B.PK_ID IS NULL 
UNION 
-- MODIFIED 
SELECT B.*, 'MODIFIED' AS 'CHANGE_TYPE' 
FROM (
     SELECT * FROM TableA 
     EXCEPT 
     SELECT * FROM TableB 
    ) S1 
INNER JOIN TableB B ON S1.PK_ID = B.PK_ID; 

Не совсем элегантно, но он работает.

+0

Это сработало для меня, спасибо. – henry000

0

Вы можете использовать предложение OUTPUT:

Возвращает информацию или выражения, основываясь на каждый ряд затронутых в INSERT, UPDATE или DELETE заявление. Эти результаты могут быть возвращены в приложение обработки для использования в таких вещах, как сообщения подтверждения, архивирование и другие подобные требования приложения. В качестве альтернативы результаты могут быть вставлены в переменную таблицы или таблицы.

См. Следующее, извините, у меня нет практического кода для вас. Но обратите внимание, что предложение SQL output может использоваться для возврата любого значения из таблиц «вставлено» и «удалено» (новое значение и старое значение) при выполнении вставки или обновления. follow this for more info

+0

Спасибо за ответ ... однако, к сожалению, TABLEA не редактируется (вставка, обновление, удаление) напрямую. Благодаря нашему документообороту данные редактируются с другими организациями, и они отправляют данные обратно нам с ZIP-файлами. Мы разархивируем файл и повторно заполняем таблицуA и сравниваем с tableB ... Я уточнил свой вопрос, чтобы уточнить это. Извинения за то, что он не разъяснил его на начальном этапе. – henry000

+0

Нет проблем для xx для ясности. –

1

Основываясь на том, что я понял, я придумал следующее решение.

DECLARE @tableA TABLE (ID INT, Number INT) 
DECLARE @tableB TABLE (ID INT, Number INT) 

INSERT INTO @tableA VALUES 
    (1,10), 
    (2,20), 
    (3,30), 
    (4,40) 

INSERT INTO @tableB VALUES 
    (1,11), 
    (2,20), 

    (4,40), 
    (5,50) 


SELECT *,'Modified or deleted' as 'Status' FROM 
(
    select * from @tableA 
    except 
    select * from @tableB 
)a WHERE ID NOT IN 
(
    select ID from @tableB 
    except 
    select ID from @tableA 
) 
UNION 
SELECT *,'New' as 'Status' FROM 
(
    select * from @tableB 
    except 
    select * from @tableA 
)b WHERE ID NOT IN 
(
    SELECT ID FROM 
    (
     select * from @tableA 
     except 
     select * from @tableB 
    )a WHERE ID NOT IN 
    (
     select ID from @tableB 
     except 
     select ID from @tableA 
    ) 
) 
+0

Спасибо - это то, что я имел в виду, но я подумал, что это кажется неэффективным, поскольку существуют утверждения «кроме», поэтому я задал вопрос здесь в SO. Все-таки проголосовало за это, так как оно работает и находится в одном-единственном заявлении. – henry000

0

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

SELECT 
    IsNull(tableB.ID,tableA.ID) as 'ID', 
    IsNull(tableB.Number,tableA.Number) as 'Number', 
    'Action' = CASE 
        WHEN tableB.ID IS NULL THEN 'Deleted' 
        WHEN tableA.ID IS NULL THEN 'Created' 
        ELSE 'Updated' 
       END 
FROM tableA 
FULL OUTER JOIN tableB 
    ON tableB.ID = tableA.ID 
WHERE 
    NOT EXISTS (SELECT tableB.* INTERSECT SELECT tableA.*) 

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

Я положил все три отсюда в скрипку, и это удивительно, как иначе они все скомпилируются.

http://sqlfiddle.com/#!6/b1a5a/5

0
declare @DBOrderItem table 
(
    OrderItemGuid UniqueIdentifier default newid(), 
    Name VarChar(100) 
); 

declare @PayloadOrderItem table 
(
    OrderItemGuid UniqueIdentifier default newid(), 
    Name VarChar(100) 
); 

insert into @DBOrderItem (Name) values ('Phone'); 
insert into @DBOrderItem (Name) values ('Laptop'); 

insert into @PayloadOrderItem 
    select top 1 * from @DBOrderItem; 
insert into @PayloadOrderItem (Name) values ('Tablet'); 


select doi.OrderItemGuid, 
    doi.Name, 
    case when poi.OrderItemGuid is null then 'Delete' else 'Update' end ActionType 
from @DBOrderItem doi 
    left join @PayloadOrderItem poi on doi.OrderItemGuid = poi.OrderItemGuid 
union 
select poi.OrderItemGuid, 
    poi.Name, 
    'Add' ActionType 
from @PayloadOrderItem poi 
    left join @DBOrderItem doi on doi.OrderItemGuid = poi.OrderItemGuid 
where doi.OrderItemGuid is null;  
Смежные вопросы