Как система отслеживания изменений обрабатывает изменения в базе данных

Некоторые приложения, использующие отслеживание изменений, выполняют двустороннюю синхронизацию с другими хранилищами данных. Это означает, что изменения в базе данных SQL Server обновляются в другом хранилище данных, а изменения в другом хранилище данных обновляются в базе данных SQL Server.

Если приложение обновляет локальную базу данных изменениями, сделанными в другом хранилище данных, оно должно выполнить следующие операции.

  • Проверить наличие конфликтов.

    Конфликт возникает, когда одни и те же данные одновременно изменяются в обоих хранилищах данных. Приложение должно иметь возможность проверить наличие конфликта и получить достаточно информации для его разрешения.

  • Сохранить контекстные сведения приложения.

    Приложение хранит данные, с которыми связана информация отслеживания изменений. Эта информация должна быть доступна вместе с другими данными отслеживания изменений, если изменения были получены из локальной базы данных. Типичный пример таких контекстных сведений – идентификатор хранилища данных, которое было источником изменений.

Для выполнения вышеуказанных операций приложение синхронизации может использовать следующие функции:

  • CHANGETABLE(VERSION…)

    Когда приложение выполняет изменения, оно может использовать эту функцию для проверки конфликтов. Функция получает последние данные отслеживания изменений в заданной строке в таблицу отслеживания изменений. Эти данные содержат версию последней измененной строки. Это позволяет приложению определить, изменилась ли строка с момента последней синхронизации приложения.

  • WITH CHANGE_TRACKING_CONTEXT

    Приложение может использовать это предложение для хранения контекстных данных.

Проверка наличия конфликтов

В сценарии двусторонней синхронизации клиентское приложение должно определить, обновлялась ли строка с момента получения приложением последних изменений.

В следующем примере показано использование функции CHANGETABLE(VERSION …) для проверки конфликтов самым эффективным способом — без отдельного запроса. В этом примере функция CHANGETABLE(VERSION …) определяет значение SYS_CHANGE_VERSION для строки, заданной параметром @product id. С помощью функции 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 приложение может хранить контекстные данные вместе с информацией об изменениях. Затем эти данные можно получить в столбце SYS_CHANGE_CONTEXT, который возвращает функция CHANGETABLE(CHANGES …).

Контекстные данные обычно используются для определения источника изменений. Если источник изменений можно определить, эти данные могут использоваться хранилищем данных, чтобы не получать изменения при повторной синхронизации.

  -- 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
ПримечаниеПримечание

Существует вероятность того, что обновляемая в рамках транзакции моментальных снимков строка уже была обновлена в другой транзакции после начала транзакции моментальных снимков. В этом случае произойдет конфликт обновления при изоляции моментальных снимков, который вызовет прекращение транзакции. В этом случае повторите попытку обновления. В дальнейшем это приведет к обнаружению конфликта отслеживания изменений и ни одна из строк обновлена не будет.