INTRODUKTION
SQL Server MERGE-satsen är ett otroligt användbart verktyg för att utföra DML-operationer baserat på att jämföra två tabeller eller två datamängder. Användning av denna sats är faktiskt som att utföra flera operationer i en enda sats.
Den här artikeln kommer att utforska tre användningsfall som gränsar till att säkerställa att data mellan en onlinetabell och en historiktabell synkroniseras.
Låt oss beskriva scenariot i några skisserade uttalanden som skulle vara bekanta för många DBA:er eller utvecklare:
- Källtabellen som heter Tran fångar transaktioner som sker i realtid på löpande basis.
- Den överenskomna lagringsperioden för tabellen Tran är en månad. Tran måste rensas i slutet av varje månad.
- Dagligen måste Trans data skickas till TranHistory under "Slut på dagen".
- TranHistory tabellen är en historisk tabell som aggregerar transaktionsdata över ett år.
- Alla inlägg och uppdateringar på Tran tabellen måste återspeglas i TranHistory tabell i slutet av varje dag.
FÖRBEREDA SCENARIOET
Det scenario som beskrivs ovan innebär att poster i Tran-tabellen också finns i tabellen TranHistory tills de rensas varje månad. Varje dag kommer det att finnas några nya poster i Tran-tabellen men INTE i TranHistory-tabellen. Vi måste hitta ett sätt att infoga dessa nya rader.
Låt oss först förbereda tabellerna i fråga (se lista 1 och 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 ,
)
NÄR DET INTE MATCHAR DÅ SÄTT IN
Med hjälp av koden i Lista 3 infogar vi några rader i Tran-tabellen för att komma igång. Sedan fortsätter vi att använda en MERGE-sats för att flytta data till TranHistory-tabellen.
-- 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
Vi kunde ha gjort detta på ett mycket enklare sätt, men vi vill visa MERGE-satssyntaxen (se lista 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
MERGE-uttalandet säger:
- Ta innehållet i tabellen [Tran] och jämför det med tabellen [TranHistory] baserat på responseId kolumn.
- Infoga de rader som du hittar i källtabellen men inte hittar i måltabellen (TranHistory).
Som det är ligger Tran och TranHistory på per. Men anta att följande dag introduceras nya rader i Trans-tabellen. Vi måste också skjuta dem till TransHistory-tabellen samtidigt som vi behåller poster i Tran-tabellen tills månaden är över.
Vi använder återigen skriptet i lista 4. Den här gången lägger vi till en OUTPUT-klausul för att berätta vad som infördes (se lista 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
Vi kan upprepa denna process efter att ha introducerat ytterligare rader i Tran-tabellen (lista 6 och 7), och vi får liknande beteende:
-- 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
NÄR MATCH UPPDATERA DÅ
Ett annat fall är en uppdatering av live-tabellen (Tran) när vi vill synkronisera sådana uppdateringar till TranHistory-tabellen. Vi skapar det här scenariot genom att uppdatera rader i Tran-tabellen med skriptet i 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
Figur 6 visar att rader uppdateras i destinationstabellen. Vi får denna detalj med hjälp av OUTPUT-satsen.
Vi måste använda klausulen som är markerad i listning 9. MERGE-satsen identifierade rader som matchar JOIN-villkoret och uppdaterar data i den angivna kolumnen (konto ). Vi kan uppdatera flera rader med detta tillvägagångssätt.
-- 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
NÄR DET INTE MATCHAS EFTER KÄLLA, TA DET UR
Ytterligare ett scenario innebär att rader tas bort från källtabellen (lista 10). När det händer måste vi identifiera de rader som inte längre finns i källtabellen och ta bort dem från måltabellen.
Vi åstadkommer detta med hjälp av klausulen som markerats i lista 10. Återigen visar figurerna 7 och 8 raderna som påverkas av denna MERGE-sats.
-- 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
Figur 7 visar raderna som raderades och figur 8 visar de som uppdaterades. Det är kraften i MERGE-förklaringen. Vi kan utföra raderings-, infognings- och uppdateringsoperationer i ett och samma uttalande.
SLUTSATS
Den här artikeln granskade användningen av MERGE-satsen för att synkronisera data mellan en onlinetabell och en historiktabell samtidigt som den önskade lagringen som krävs i båda tabellerna bibehålls.
Det finns många andra användningsfall för SQL Server MERGE-satser som inte täcks i den här artikeln, men de utforskas i Microsofts dokumentation. Källdata som anges i USING-satsen är inte begränsade till tabeller. Resultatuppsättningar från explicita SELECT-satser kan vara källdata. Vanliga tabelluttryck kan också vara källdata.
REFERENSER
MERGE i Transact-SQL