EINFÜHRUNG
Die MERGE-Anweisung von SQL Server ist ein unglaublich nützliches Tool zum Ausführen von DML-Operationen, die auf dem Vergleichen von zwei Tabellen oder zwei Datensätzen basieren. Die Verwendung dieser Anweisung ist eigentlich so, als würde man mehrere Operationen in einer einzigen Anweisung ausführen.
In diesem Artikel werden drei Anwendungsfälle untersucht, die an die Sicherstellung der Synchronisierung von Daten zwischen einer Online-Tabelle und einer Verlaufstabelle grenzen.
Lassen Sie uns das Szenario in einigen skizzierten Aussagen beschreiben, die vielen DBAs oder Entwicklern bekannt sein dürften:
- Die Quelltabelle namens Tran erfasst laufend Transaktionen in Echtzeit.
- Die vereinbarte Aufbewahrungsfrist für die Tabelle Tran beträgt einen Monat. Tran muss am Ende jedes Monats geleert werden.
- Täglich müssen Trans Daten an TranHistory gepusht werden während des „Tagesabschlusses“.
- Die TranHistory Tabelle ist eine historische Tabelle, die Transaktionsdaten über ein Jahr zusammenfasst.
- Alle Einfügungen und Aktualisierungen auf Tran Tabelle muss sich in der TranHistory widerspiegeln Tisch am Ende eines jeden Tages.
VORBEREITUNG DES SZENARIOS
Das oben beschriebene Szenario impliziert, dass Datensätze in der Tran-Tabelle auch in der Tabelle TranHistory vorhanden sind, bis sie monatlich gelöscht werden. Jeden Tag gibt es ein paar neue Datensätze in der Tran-Tabelle, aber NICHT in der TranHistory-Tabelle. Wir müssen einen Weg finden, diese neuen Zeilen einzufügen.
Lassen Sie uns zunächst die betreffenden Tabellen vorbereiten (siehe Listing 1 und 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 ,
)
WENN NICHT ÜBEREINSTIMMT, DANN EINFÜGEN
Mit dem Code in Listing 3 fügen wir zunächst einige Zeilen in die Tran-Tabelle ein. Dann verwenden wir eine MERGE-Anweisung, um die Daten in die TranHistory-Tabelle zu verschieben.
-- 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
Wir hätten das auch viel einfacher machen können, aber wir wollen die Syntax der MERGE-Anweisung zeigen (siehe 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
Die MERGE-Anweisung lautet:
- Nehmen Sie den Inhalt der Tabelle [Tran] und vergleichen Sie ihn anhand der responseId mit der Tabelle [TranHistory]. Spalte.
- Fügen Sie die Zeilen ein, die Sie in der Quelltabelle finden, aber nicht in der Zieltabelle (TranHistory).
So wie es ist, sind Tran und TranHistory auf pro. Angenommen, am nächsten Tag werden neue Zeilen in die Trans-Tabelle eingefügt. Wir müssen sie auch in die TransHistory-Tabelle verschieben, während die Aufzeichnungen in der Tran-Tabelle aufbewahrt werden, bis der Monat vorbei ist.
Wir verwenden wieder das Skript in Listing 4. Dieses Mal fügen wir eine OUTPUT-Klausel hinzu, um mitzuteilen, was eingeführt wurde (siehe 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
Wir können diesen Vorgang wiederholen, nachdem wir zusätzliche Zeilen in die Tran-Tabelle eingefügt haben (Listing 6 und 7), und wir erhalten ein ähnliches Verhalten:
-- 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
WENN ÜBEREINSTIMMT, DANN AKTUALISIEREN
Ein anderer Fall ist eine Aktualisierung der Live-Tabelle (Tran), wenn wir solche Aktualisierungen mit der TranHistory-Tabelle synchronisieren möchten. Wir erstellen dieses Szenario, indem wir Zeilen in der Tran-Tabelle mit dem Skript in Listing 8 aktualisieren.
-- 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
Abbildung 6 zeigt, dass Zeilen in der Zieltabelle aktualisiert werden. Dieses Detail erhalten wir mit der OUTPUT-Klausel.
Wir müssen die in Listing 9 hervorgehobene Klausel verwenden. Die MERGE-Anweisung identifizierte Zeilen, die der JOIN-Bedingung entsprechen, und aktualisiert die Daten in der angegebenen Spalte (account ). Mit diesem Ansatz können wir mehrere Zeilen aktualisieren.
-- 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
WENN NICHT MIT QUELLE ÜBEREINSTIMMT, DANN LÖSCHEN
In einem weiteren Szenario werden Zeilen aus der Quelltabelle gelöscht (Listing 10). Wenn dies passiert, müssen wir die Zeilen identifizieren, die nicht mehr in der Quelltabelle vorhanden sind, und sie aus der Zieltabelle entfernen.
Wir erreichen dies mit der in Listing 10 hervorgehobenen Klausel. Auch hier zeigen die Abbildungen 7 und 8 die Zeilen, die von dieser MERGE-Anweisung betroffen sind.
-- 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
Abbildung 7 zeigt die Zeilen, die gelöscht wurden, und Abbildung 8 zeigt die aktualisierten. Es ist die Macht der MERGE-Anweisung. Wir können Lösch-, Einfüge- und Aktualisierungsoperationen in einer einzigen Anweisung ausführen.
SCHLUSSFOLGERUNG
In diesem Artikel wurde die Verwendung der MERGE-Anweisung zum Synchronisieren von Daten zwischen einer Online-Tabelle und einer Verlaufstabelle unter Beibehaltung der gewünschten Aufbewahrung in beiden Tabellen überprüft.
Es gibt viele andere Anwendungsfälle für MERGE-Anweisungen von SQL Server, die in diesem Artikel nicht behandelt werden, aber sie werden in der Microsoft-Dokumentation untersucht. In der USING-Klausel angegebene Quelldaten sind nicht auf Tabellen beschränkt. Ergebnismengen von expliziten SELECT-Anweisungen können Quelldaten sein. Auch allgemeine Tabellenausdrücke können Quelldaten sein.
REFERENZEN
MERGE in Transact-SQL