次の方法で共有


変更の追跡でデータベースへの変更が処理されるしくみ

変更の追跡を使用するアプリケーションの中には、別のデータ ストアとの双方向の同期を行うものもあります。この場合、SQL Server データベースで行われた変更が他のデータ ストアで更新され、他のデータ ストアで行われた変更が SQL Server データベースで更新されます。

別のデータ ストアからの変更でローカル データベースを更新する際には、アプリケーションで次の操作を実行する必要があります。

  • 競合がないかどうかを確認します。

    両方のデータ ストアで同じデータが同時に変更されると、競合が発生します。アプリケーションで、競合がないかどうかを確認したり、競合を解決するための十分な情報を取得したりできる必要があります。

  • アプリケーション コンテキスト情報を格納します。

    変更追跡情報を持つデータをアプリケーションで格納します。この情報は、変更がローカル データベースから取得されるときに他の変更追跡情報と共に取得できます。たとえば変更のソースとなったデータ ストアの ID は、このコンテキスト情報の一例です。

同期アプリケーションでこれらの操作を実行するには、次の関数を使用できます。

  • CHANGETABLE(VERSION…)

    アプリケーションで変更を加える際にこの関数を使用すると、競合がないかどうかを確認できます。この関数は、変更追跡対象テーブルの指定された行に関する最新の変更追跡情報を取得します。この変更追跡情報には、最後に変更された行のバージョンが含まれています。この情報をアプリケーションで使用することによって、アプリケーションの前回の同期後に行が変更されたかどうかを確認できます。

  • WITH CHANGE_TRACKING_CONTEXT

    アプリケーションでこの句を使用すると、コンテキスト データを格納できます。

競合の確認

双方向同期のシナリオでは、前回変更を取得してから行が更新されていないかどうかをクライアント アプリケーションで確認する必要があります。

次の例は、CHANGETABLE(VERSION …) 関数を使用して最も効率的に (別のクエリを使用せずに) 競合を確認する方法を示しています。この例では、CHANGETABLE(VERSION …) により、@product id で指定した行の SYS_CHANGE_VERSION が確認されます。CHANGETABLE(CHANGES …) でも同じ情報を取得できますが、これほど効率的ではありません。行の SYS_CHANGE_VERSION の値が @last_sync_version の値より大きい場合は競合があります。競合がある場合、行は更新されません。ISNULL() の確認が必要なのは、行の変更情報がない場合もあるためです。変更の追跡を有効にしてからまだ行が更新されていない場合や、変更情報がクリーンアップされてからまだ行が更新されていない場合は、変更情報は存在しません。

-- Assumption: @last_sync_version has been validated.

UPDATE
    SalesLT.Product
SET
    ListPrice = @new_listprice
FROM
    SalesLT.Product AS P
WHERE
    ProductID = @product_id AND
    @last_sync_version >= ISNULL (
        SELECT CT.SYS_CHANGE_VERSION
        FROM CHANGETABLE(VERSION SalesLT.Product,
                        (ProductID), (P.ProductID)) AS CT),
        0)

次のコードでは、更新された行の数を確認して、競合に関する詳細情報を特定できます。

-- If the change cannot be made, find out more information.
IF (@@ROWCOUNT = 0)
BEGIN
    -- Obtain the complete change information for the row.
    SELECT
        CT.SYS_CHANGE_VERSION, CT.SYS_CHANGE_CREATION_VERSION,
        CT.SYS_CHANGE_OPERATION, CT.SYS_CHANGE_COLUMNS
    FROM
        CHANGETABLE(CHANGES SalesLT.Product, @last_sync_version) AS CT
    WHERE
        CT.ProductID = @product_id;

    -- Check CT.SYS_CHANGE_VERSION to verify that it really was a conflict.
    -- Check CT.SYS_CHANGE_OPERATION to determine the type of conflict:
    -- update-update or update-delete.
    -- The row that is specified by @product_id might no longer exist 
    -- if it has been deleted.
END

コンテキスト情報の設定

WITH CHANGE_TRACKING_CONTEXT 句を使用すると、アプリケーションで変更情報と一緒にコンテキスト情報を格納できます。格納した情報は、CHANGETABLE(CHANGES …) によって返される SYS_CHANGE_CONTEXT 列から取得できます。

コンテキスト情報は通常、変更のソースを識別するために使用されます。変更のソースを識別できれば、その情報をデータ ストアで使用して、再び同期される際に変更が取得されないようにすることができます。

  -- Try to update the row and check for a conflict.
  WITH CHANGE_TRACKING_CONTEXT (@source_id)
  UPDATE
     SalesLT.Product
  SET
      ListPrice = @new_listprice
  FROM
      SalesLT.Product AS P
  WHERE
     ProductID = @product_id AND
     @last_sync_version >= ISNULL (
         (SELECT CT.SYS_CHANGE_VERSION FROM CHANGETABLE(VERSION SalesLT.Product,
         (ProductID), (P.ProductID)) AS CT),
         0)

一貫性のある正しい結果の確保

アプリケーションで @last_sync_version の値を検証するときにはクリーンアップ プロセスを考慮に入れる必要があります。これは、CHANGE_TRACKING_MIN_VALID_VERSION() が呼び出された後、更新が行われる前に、データが削除される可能性があるからです。

重要な注意事項重要

スナップショット分離を使用してスナップショット トランザクション内で変更を行うことをお勧めします。

-- Prerequisite is to ensure ALLOW_SNAPSHOT_ISOLATION is ON for the database.

SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRAN
    -- Verify that last_sync_version is valid.
    IF (@last_sync_version <
CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID(‘SalesLT.Product’)))
    BEGIN
       RAISERROR (N’Last_sync_version too old’, 16, -1);
    END
    ELSE
    BEGIN
        -- Try to update the row.
        -- Check @@ROWCOUNT and check for a conflict.
    END
COMMIT TRAN
注意

スナップショット トランザクション内で更新の対象になっている行は、スナップショット トランザクションの開始後に別のトランザクションで更新されている可能性があります。この場合はスナップショット分離の更新の競合が発生し、トランザクションは終了します。この状況が発生した場合は、更新を再試行してください。その結果、変更の追跡の競合が検出されることになり、行は変更されません。