INLEIDING
De SQL Server MERGE-instructie is een ongelooflijk handig hulpmiddel voor het uitvoeren van DML-bewerkingen op basis van het vergelijken van twee tabellen of twee gegevenssets. Het gebruik van deze instructie is eigenlijk hetzelfde als het uitvoeren van meerdere bewerkingen in een enkele instructie.
Dit artikel onderzoekt drie gebruiksscenario's die grenzen aan het synchroniseren van gegevens tussen een online tabel en een geschiedenistabel.
Laten we het scenario beschrijven in een paar geschetste uitspraken die veel DBA's of ontwikkelaars bekend zullen zijn:
- De brontabel genaamd Tran legt doorlopend transacties vast die in realtime plaatsvinden.
- De overeengekomen bewaartermijn voor tafel Tran is één maand. Tran moet aan het einde van elke maand worden verwijderd.
- Dagelijks moeten de gegevens van Tran naar TranHistory worden gepusht tijdens het proces "Einde van de dag".
- De TranHistory tabel is een historische tabel die transactiegegevens over een jaar samenvoegt.
- Alle toevoegingen en updates op de Tran tabel moet weerspiegelen in de TranHistory tafel aan het einde van elke dag.
HET SCENARIO VOORBEREIDEN
Het hierboven beschreven scenario houdt in dat records in de Tran-tabel ook in de tabel TranHistory voorkomen totdat ze maandelijks worden gewist. Elke dag zullen er een paar nieuwe records in de Tran-tabel zijn, maar NIET in de TranHistory-tabel. We moeten een manier vinden om deze nieuwe rijen in te voegen.
Laten we eerst de tabellen in kwestie voorbereiden (zie lijst 1 en 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 ,
)
WANNEER NIET OVEREENGEKOMEN, DAN INVOEGEN
Met behulp van de code in Listing 3 voegen we een paar rijen in de Tran-tabel in om aan de slag te gaan. Vervolgens gaan we verder met het gebruik van een MERGE-instructie om de gegevens naar de TranHistory-tabel te verplaatsen.
-- 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
We hadden dit op een veel eenvoudigere manier kunnen doen, maar we willen de syntaxis van de MERGE-instructie laten zien (zie lijst 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
De MERGE-instructie zegt:
- Neem de inhoud van de [Tran]-tabel en vergelijk deze met de [TranHistory]-tabel op basis van de responseId kolom.
- Voeg de rijen in die u in de brontabel vindt, maar niet vindt in de doeltabel (TranHistory).
Zoals het is, zijn Tran en TranHistory op per. Maar stel dat de volgende dag nieuwe rijen worden geïntroduceerd in de Trans-tabel. We moeten ze ook naar de TransHistory-tabel pushen terwijl we de records in de Tran-tabel bewaren tot de maand voorbij is.
We gebruiken opnieuw het script in listing 4. Deze keer voegen we een OUTPUT-clausule toe om te vertellen wat er is geïntroduceerd (zie 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
We kunnen dit proces herhalen nadat we extra rijen aan de Tran-tabel hebben toegevoegd (lijst 6 en 7), en we krijgen soortgelijk gedrag:
-- 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
WANNEER OVEREENGEKOMEN DAN UPDATE
Een ander geval is een update van de live-tabel (Tran) wanneer we dergelijke updates willen synchroniseren met de TranHistory-tabel. We creëren dit scenario door rijen in de Tran-tabel bij te werken met behulp van het script in Listing 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
Afbeelding 6 laat zien dat rijen worden bijgewerkt in de doeltabel. We krijgen dit detail met behulp van de OUTPUT-clausule.
We moeten de clausule gebruiken die is gemarkeerd in Listing 9. De MERGE-instructie identificeerde rijen die overeenkomen met de JOIN-voorwaarde en werkt de gegevens bij in de opgegeven kolom (account ). Met deze aanpak kunnen we meerdere rijen bijwerken.
-- 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
WANNEER NIET OVEREENGEKOMEN DOOR BRON, VERWIJDER DAN
Nog een scenario houdt in dat rijen worden verwijderd uit de brontabel (lijst 10). Wanneer dit gebeurt, moeten we de rijen identificeren die niet meer bestaan in de brontabel en ze verwijderen uit de doeltabel.
We bereiken dit met behulp van de clausule die is gemarkeerd in Listing 10. Nogmaals, Figuren 7 en 8 tonen de rijen die worden beïnvloed door deze MERGE-instructie.
-- 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
Afbeelding 7 toont de rijen die zijn verwijderd en Afbeelding 8 toont de rijen die zijn bijgewerkt. Het is de kracht van de MERGE-instructie. We kunnen bewerkingen voor verwijderen, invoegen en bijwerken allemaal in één verklaring uitvoeren.
CONCLUSIE
In dit artikel wordt het gebruik van de MERGE-instructie besproken om gegevens tussen een online tabel en een geschiedenistabel te synchroniseren, terwijl de gewenste retentie in beide tabellen behouden blijft.
Er zijn veel andere gebruiksscenario's voor SQL Server MERGE-instructies die niet in dit artikel worden behandeld, maar ze worden onderzocht in de Microsoft-documentatie. Brongegevens die zijn gespecificeerd in de USING-clausule zijn niet beperkt tot tabellen. Resultatensets van expliciete SELECT-instructies kunnen brongegevens zijn. Algemene tabeluitdrukkingen kunnen ook brongegevens zijn.
REFERENTIES
SAMENVOEGEN in Transact-SQL