sql >> データベース >  >> RDS >> Sqlserver

SQL Server MERGEステートメントのユースケース:オンラインテーブルと履歴テーブルの同期

    はじめに

    SQL Server MERGEステートメントは、2つのテーブルまたは2つのデータセットの比較に基づいてDML操作を実行するための非常に便利なツールです。このステートメントの使用法は、実際には1つのステートメントで複数の操作を実行するようなものです。

    この記事では、オンラインテーブルと履歴テーブルの間のデータを確実に同期させることに隣接する3つのユースケースについて説明します。

    多くのDBAや開発者に馴染みのあるいくつかの概説されたステートメントでシナリオを説明しましょう:

    1. Tranというソーステーブル 継続的にリアルタイムで発生するトランザクションをキャプチャします。
    2. テーブルTranの合意された保持期間は1か月です。トランは月末にパージする必要があります。
    3. 毎日、Tranのデータを TranHistoryにプッシュする必要があります 「一日の終わり」のプロセス中。
    4. TranHistory tableは、1年間のトランザクションデータを集約した履歴テーブルです。
    5. Tranのすべての挿入と更新 テーブルはTranHistoryに反映する必要があります 毎日の終わりにテーブル。

    シナリオの準備

    上記のシナリオは、Tranテーブルのレコードが、毎月パージされるまでテーブルTranHistoryにも存在することを意味します。毎日、Tranテーブルにはいくつかの新しいレコードがありますが、TranHistoryテーブルにはありません。これらの新しい行を挿入する方法を見つける必要があります。

    まず、問題のテーブルを準備しましょう(リスト1と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 ,
    )
    

    一致しない場合は挿入

    リスト3のコードを使用して、Tranテーブルにいくつかの行を挿入して開始します。次に、MERGEステートメントを使用してデータを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
    

    これはもっと簡単な方法で行うこともできますが、MERGEステートメントの構文を示したいと思います(リスト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ステートメントは次のように述べています:

    1. [Tran]テーブルの内容を取得し、 responseIdに基づいて[TranHistory]テーブルと比較します。 列。
    2. ソーステーブルにはあるがターゲットテーブルにはない行を挿入します(TranHistory)。

    現状では、TranとTranHistoryはそれぞれです。しかし、翌日、トランステーブルに新しい行が導入されたとします。また、1か月が終わるまで、Tranテーブルにレコードを保持しながら、それらをTransHistoryテーブルにプッシュする必要があります。

    リスト4のスクリプトを再び使用します。今回は、何が導入されたかを示すOUTPUT句を追加します(リスト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
    

    Tranテーブル(リスト6および7)に追加の行を導入した後、このプロセスを繰り返すことができ、同様の動作が得られます:

    -- 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
    

    一致してから更新する場合

    もう1つのケースは、ライブテーブル(Tran)を更新して、そのような更新をTranHistoryテーブルに同期する場合です。リスト8のスクリプトを使用して、Tranテーブルの行を更新することにより、このシナリオを作成します。

    -- 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
    

    図6は、宛先テーブルの行が更新されていることを示しています。この詳細は、OUTPUT句を使用して取得します。

    リスト9で強調表示されている句を使用する必要があります。MERGEステートメントは、JOIN条件に一致する行を識別し、指定された列( account )のデータを更新します。 )。このアプローチで複数の行を更新できます。

    -- 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
    

    ソースが一致しない場合は削除

    もう1つのシナリオでは、ソーステーブルから行が削除されます(リスト10)。その場合は、ソーステーブルに存在しなくなった行を特定し、宛先テーブルから削除する必要があります。

    これは、リスト10で強調表示されている句を使用して実現します。ここでも、図7と8は、この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
    

    図7は削除された行を示し、図8は更新された行を示しています。それはです MERGEステートメントの。削除、挿入、更新のすべての操作を1つのステートメントで実行できます。

    結論

    この記事では、MERGEステートメントを使用して、オンラインテーブルと履歴テーブルの間でデータを同期し、両方のテーブルで必要な保持を維持する方法について説明しました。

    この記事で取り上げられていないSQLServerMERGEステートメントの使用例は他にもたくさんありますが、それらはMicrosoftのドキュメントで詳しく説明されています。 USING句で指定されたソースデータはテーブルに限定されません。明示的なSELECTステートメントの結果セットはソースデータにすることができます。一般的なテーブル式はソースデータにすることもできます。

    参考資料

    Transact-SQLでのマージ


    1. ComboBox.ValueMemberおよびDisplayMember

    2. ディメンションのディメンション:データウェアハウスの最も一般的なディメンションテーブルタイプの概要

    3. NVLとCoalesceのOracleの違い

    4. SELECT ... FORUPDATEを使用する場合