WPROWADZENIE
Instrukcja SQL Server MERGE jest niezwykle przydatnym narzędziem do wykonywania operacji DML w oparciu o porównanie dwóch tabel lub dwóch zestawów danych. Użycie tej instrukcji jest w rzeczywistości jak wykonywanie wielu operacji w jednej instrukcji.
W tym artykule omówimy trzy przypadki użycia, które graniczą z zapewnieniem synchronizacji danych między tabelą online a tabelą historii.
Opiszmy ten scenariusz w kilku nakreślonych stwierdzeniach, które byłyby znane wielu administratorom baz danych lub programistom:
- Tabela źródłowa o nazwie Transfer na bieżąco rejestruje transakcje odbywające się w czasie rzeczywistym.
- Uzgodniony okres przechowywania dla tabeli Tran wynosi jeden miesiąc. Tran musi być czyszczony pod koniec każdego miesiąca.
- Codziennie dane Trana muszą być przesyłane do TranHistory podczas procesu „Koniec dnia”.
- TranHistory table to historyczna tabela agregująca dane transakcji z okresu jednego roku.
- Wszystkie wstawki i aktualizacje w Transu tabela musi odzwierciedlać w TranHistory stolik na koniec każdego dnia.
PRZYGOTOWANIE SCENARIUSZA
Opisany powyżej scenariusz sugeruje, że rekordy w tabeli Tran istnieją również w tabeli TranHistory, dopóki nie zostaną usunięte co miesiąc. Każdego dnia w tabeli Tran pojawi się kilka nowych rekordów, ale NIE w tabeli TranHistory. Musimy znaleźć sposób na wstawienie tych nowych wierszy.
Najpierw przygotujmy odpowiednie tabele (patrz Listing 1 i 2).
-- Listing 1: Create Tran Table
USE AU
GO
CREATE TABLE [Tran] (
responseId int NOT NULL ,
senderId varchar(15) ,
msisdn varchar(15) ,
[message] varbinary ,
status smallint ,
application varchar ,
receivedTime timestamp NULL ,
processedTime datetime2 NULL ,
flag smallint ,
requestDelivery smallint ,
delivered smallint ,
account varchar(20) ,
srcTon smallint ,
srcNpi smallint ,
destTon smallint ,
destNpi smallint ,
errorCode smallint ,
messageId varchar ,
sequenceNo int ,
retries smallint ,
messagePriority int ,
userId varchar(20) ,
bulkId varchar(20)
)
-- Listing 2: Create TranHistory Table
USE AU
GO
CREATE TABLE [TranHistory] (
responseId int NOT NULL ,
senderId varchar(15) ,
msisdn varchar(15) ,
[message] varchar(160) ,
status smallint ,
application varchar ,
receivedTime datetime2 NULL ,
processedTime datetime2 NULL ,
flag smallint ,
requestDelivery smallint ,
delivered smallint ,
account varchar(20) ,
srcTon smallint ,
srcNpi smallint ,
destTon smallint ,
destNpi smallint ,
errorCode smallint ,
messageId varchar ,
sequenceNo int ,
retries smallint ,
messagePriority int ,
userId varchar(20) ,
bulkId varchar(20) ,
archivedTime datetime2 NOT NULL ,
)
GDY NIE DOPASOWANO, WSTAWIĆ
Korzystając z kodu z Listingu 3, wstawiamy kilka wierszy w tabeli Tran, aby rozpocząć. Następnie przechodzimy do użycia instrukcji MERGE, aby przenieść dane do tabeli TranHistory.
-- Listing 3: Insert Initial Set of Rows in Tran Table
USE [AU]
GO
INSERT INTO [dbo].[Tran]
VALUES
(8000,'0233456789','Wishing you a Happy New Year',1,'K','20201110 15:00:00','20201110 15:10:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(7777,'0233456789','The blessing of the Lord be with you',1,'K','20201110 08:00:00','20201110 08:10:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(7005,'0234876789','Happy Birthday to you',1,'K','20201110 09:00:00','20201110 09:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(9002,'0233456789','Merry Christmas',1,'K','20201110 07:00:00','20201110 07:15:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(6789,'0233467889','Buy our brand new cars for less than $8000',1,'K','20201110 14:00:00','20201110 14:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(7685,'0244556789','Happy New Month. God bless and increase you',1,'K','20201110 17:00:00','20201110 17:08:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(4983,'0229856789','Help is coming your way today!',1,'K','20201110 19:00:00','20201110 19:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(6879,'0239986789','Call us for your next relocation project',1,'K','20201110 19:15:00','20201110 19:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(4567,'0233456789','Hard Work Always Pays',1,'K','20201110 22:05:00','20201110 22:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(8890,'0244656733','Don''t wait to buy land, buy land and wait',1,'K','20201110 15:05:00','20201110 15:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(6789,'0233466734','We are relocating. Call us on 077788997',1,'K','20201110 18:02:00','20201110 18:17:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(9899,'0233456556','Watch out for our latest movie',1,'K','20201110 06:00:00','20201110 06:02:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(6789,'0233456338','We are here to make you happy',1,'K','20201110 12:16:00','20201110 12:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
GO
Moglibyśmy to zrobić w znacznie prostszy sposób, ale chcemy pokazać składnię instrukcji MERGE (patrz Listing 4):
-- Listing 4: Merge Records in Tran Table to TranHistory Table
MERGE INTO [TranHistory] a USING [Tran] b
ON a.responseId=b.responseID
WHEN NOT MATCHED BY TARGET THEN INSERT
([responseId],[senderId],[msisdn],[message],[status],[application],[receivedTime],[processedTime],[flag],[requestDelivery],[delivered],[account],[srcTon],[srcNpi],[destTon],[destNpi],[errorCode],[messageId],[sequenceNo],[retries],[messagePriority],[userId],[bulkId],[archivedTime])
VALUES
([responseId],[senderId],[msisdn],[message],[status],[application],[receivedTime],[processedTime],[flag],[requestDelivery],[delivered],[account],[srcTon],[srcNpi],[destTon],[destNpi],[errorCode],[messageId],[sequenceNo],[retries],[messagePriority],[userId],[bulkId],getdate());
GO
Oświadczenie MERGE mówi:
- Weź zawartość tabeli [Tran] i porównaj ją z tabelą [TranHistory] na podstawie responseId kolumna.
- Wstaw te wiersze, które znajdziesz w tabeli źródłowej, ale których nie znajdziesz w tabeli docelowej (TranHistory).
W obecnej sytuacji Tran i TranHistory są na os. Załóżmy jednak, że następnego dnia w Tabeli Trans zostaną wprowadzone nowe wiersze. Musimy również przesunąć je do tabeli TransHistory, zachowując rekordy w tabeli Tran do końca miesiąca.
Ponownie używamy skryptu z listingu 4. Tym razem dodajemy klauzulę OUTPUT, aby poinformować, co zostało wprowadzone (patrz Listing 5):
-- Listing 5: Merge Records in Tran Table to TranHistory Table (Add OUTPUT Clause)
USE AU
GO
MERGE INTO [TranHistory] a USING [Tran] b
ON a.responseId=b.responseID
WHEN NOT MATCHED BY TARGET THEN INSERT
([responseId],[senderId],[msisdn],[message],[status],[application],[receivedTime],[processedTime],[flag],[requestDelivery],[delivered],[account],[srcTon],[srcNpi],[destTon],[destNpi],[errorCode],[messageId],[sequenceNo],[retries],[messagePriority],[userId],[bulkId],[archivedTime])
VALUES
([responseId],[senderId],[msisdn],[message],[status],[application],[receivedTime],[processedTime],[flag],[requestDelivery],[delivered],[account],[srcTon],[srcNpi],[destTon],[destNpi],[errorCode],[messageId],[sequenceNo],[retries],[messagePriority],[userId],[bulkId],getdate())
OUTPUT deleted.*, $action, inserted.*;
GO
Możemy powtórzyć ten proces po wprowadzeniu dodatkowych wierszy do tabeli Tran (Listing 6 i 7) i uzyskamy podobne zachowanie:
-- Listing 6: Insert Six New Rows in Tran Table
USE [AU]
GO
INSERT INTO [dbo].[Tran]
VALUES
(6879,'0239986789','Call us for your next relocation project',1,'K','20201110 19:15:00','20201110 19:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(4567,'0233456789','Hard Work Always Pays',1,'K','20201110 22:05:00','20201110 22:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(8890,'0244656733','Don''t wait to buy land, buy land and wait',1,'K','20201110 15:05:00','20201110 15:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(6789,'0233466734','We are relocating. Call us on 077788997',1,'K','20201110 18:02:00','20201110 18:17:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(9899,'0233456556','Watch out for our latest movie',1,'K','20201110 06:00:00','20201110 06:02:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(6789,'0233456338','We are here to make you happy',1,'K','20201110 12:16:00','20201110 12:20:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
GO
-- Listing 7: Insert Additional Three Rows in Tran Table
USE [AU]
GO
INSERT INTO [dbo].[Tran]
VALUES
(7789,'0233456433','Are you ready for your next level?',1,'K','20201110 14:35:00','20201110 14:40:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(8000,'0233457759','Hutchies Honey, another level of taste',1,'K','20201110 08:00:00','20201110 08:08:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(7777,'0233458909','Make sure you vote tomorrow',1,'K','20201110 09:45:00','20201110 09:50:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
,(9890,'0233459994','Wishing you a Merry Christmas',1,'K','20201110 10:00:00','20201110 10:05:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
GO
PO DOPASOWANIU, AKTUALIZUJ
Innym przypadkiem jest aktualizacja tabeli na żywo (Tran), gdy chcemy zsynchronizować takie aktualizacje z tabelą TranHistory. Scenariusz ten tworzymy, aktualizując wiersze w tabeli Tran za pomocą skryptu z Listingu 8.
-- Listing 8: Update and Insert Rows in Tran Table
USE AU
GO
UPDATE [dbo].[Tran] SET account='JUNIPER'
WHERE account='KAIROS';
GO
INSERT INTO [dbo].[Tran]
VALUES
(5578,'0233566933','Newest on the Block!',1,'K','20201110 14:35:00','20201110 14:40:00',1,1,1,'KAIROS',1,2,3,4,1,1,9789,2,1,'ROUTEMOBILE','9988776')
GO
Rysunek 6 pokazuje, że wiersze w tabeli docelowej są aktualizowane. Ten szczegół uzyskujemy za pomocą klauzuli OUTPUT.
Musimy użyć klauzuli wyróżnionej na Listingu 9. Wyciąg MERGE identyfikuje wiersze, które spełniają warunek JOIN i aktualizuje dane w określonej kolumnie (konto ). W ten sposób możemy zaktualizować wiele wierszy.
-- Listing 9: Sync Data in TranHistory by Updating Rows
USE AU
GO
MERGE INTO [TranHistory] a USING [Tran] b
ON a.responseId=b.responseID
WHEN MATCHED THEN UPDATE
SET a.account=b.account
WHEN NOT MATCHED BY TARGET THEN INSERT
([responseId],[senderId],[msisdn],[message],[status],[application],[receivedTime],[processedTime],[flag],[requestDelivery],[delivered],[account],[srcTon],[srcNpi],[destTon],[destNpi],[errorCode],[messageId],[sequenceNo],[retries],[messagePriority],[userId],[bulkId],[archivedTime])
VALUES
([responseId],[senderId],[msisdn],[message],[status],[application],[receivedTime],[processedTime],[flag],[requestDelivery],[delivered],[account],[srcTon],[srcNpi],[destTon],[destNpi],[errorCode],[messageId],[sequenceNo],[retries],[messagePriority],[userId],[bulkId],getdate())
OUTPUT deleted.*, $action, inserted.*;
GO
GDY NIE DOPASOWANO WEDŁUG ŹRÓDŁA, USUŃ
Jeszcze jeden scenariusz obejmuje usuwanie wierszy z tabeli źródłowej (Listing 10). Kiedy tak się dzieje, musimy zidentyfikować wiersze, które już nie istnieją w tabeli źródłowej i usunąć je z tabeli docelowej.
Osiągamy to za pomocą klauzuli wyróżnionej na Listingu 10. Ponownie, rysunki 7 i 8 pokazują wiersze, na które wpływa instrukcja MERGE.
-- Listing 10: Update and Insert Rows in Tran Table
USE AU
GO
DELETE FROM [dbo].[Tran]
WHERE account='JUNIPER';
GO
-- Listing 11: Syncing Tables After Deleting from Tran Table
USE AU
GO
MERGE INTO [TranHistory] a USING [Tran] b
ON a.responseId=b.responseID
WHEN NOT MATCHED BY SOURCE THEN DELETE
WHEN MATCHED THEN UPDATE
SET a.account=b.account
WHEN NOT MATCHED BY TARGET THEN INSERT
([responseId],[senderId],[msisdn],[message],[status],[application],[receivedTime],[processedTime],[flag],[requestDelivery],[delivered],[account],[srcTon],[srcNpi],[destTon],[destNpi],[errorCode],[messageId],[sequenceNo],[retries],[messagePriority],[userId],[bulkId],[archivedTime])
VALUES
([responseId],[senderId],[msisdn],[message],[status],[application],[receivedTime],[processedTime],[flag],[requestDelivery],[delivered],[account],[srcTon],[srcNpi],[destTon],[destNpi],[errorCode],[messageId],[sequenceNo],[retries],[messagePriority],[userId],[bulkId],getdate())
OUTPUT deleted.*, $action, inserted.*;
GO
Rysunek 7 pokazuje wiersze, które zostały usunięte, a Rysunek 8 pokazuje te, które zostały zaktualizowane. To jest moc oświadczenia MERGE. Możemy wykonać operacje usuwania, wstawiania i aktualizowania w jednym oświadczeniu.
WNIOSEK
W tym artykule omówiono użycie instrukcji MERGE do synchronizowania danych między tabelą online a tabelą historii przy zachowaniu pożądanego przechowywania wymaganego w obu tabelach.
Istnieje wiele innych przypadków użycia instrukcji MERGE programu SQL Server, które nie zostały omówione w tym artykule, ale zostały one zbadane w dokumentacji firmy Microsoft. Dane źródłowe określone w klauzuli USING nie ograniczają się do tabel. Zestawy wyników z jawnych instrukcji SELECT mogą być danymi źródłowymi. Typowe wyrażenia tabelowe mogą również być danymi źródłowymi.
REFERENCJE
MERGE w Transact-SQL