Получение изменений с помощью функций отслеживания изменений

В этом разделе описаны функции отслеживания изменений в SQL Server 2008 и их использование для получения изменений в базе данных и сведений об этих изменениях.

Сведения о функциях отслеживания изменений

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

  • Функция CHANGETABLE(CHANGES …)
    Эта функция, возвращающая набор строк, используется в запросе данных отслеживания изменений. Функция запрашивает данные, хранящиеся во внутренних таблицах отслеживания изменений. Она возвращает результирующий набор, содержащий первичные ключи измененных строк, а также другие сведения: операцию, число обновленных столбцов и версии строк.

    Функция CHANGETABLE(CHANGES …) получает в качестве аргумента последнюю версию синхронизации. Версию последней синхронизации можно получить из переменной @last_synchronization_version, как показано в примерах этого раздела. Семантика последней версии синхронизации выглядит следующим образом.

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

    • Функция CHANGETABLE(CHANGES …) поэтому возвращает сведения обо всех изменениях, произошедших после последней версии синхронизации.

      Ниже показано, как функция CHANGETABLE(CHANGES …) используется для получения изменений.

      Пример результатов запроса на отслеживание изменений

  • Функция CHANGE_TRACKING_CURRENT_VERSION()
    Используется для получения текущей версии, которая будет применяться при следующем запросе изменений. Эта версия представляет версию последней зафиксированной транзакции.

  • Функция CHANGE_TRACKING_MIN_VALID_VERSION()
    Используется для получения минимальной допустимой версии, имеющейся у клиента, при которой он еще сможет получать достоверные результаты из функции CHANGETABLE(). Клиент должен сравнить последнюю версию синхронизации со значением, возвращенным этой функцией. Если номер последней версии синхронизации меньше, чем возвращает эта функция, то он не сможет получить достоверные результаты из функции CHANGETABLE() и должен повторно инициализировать данные.

Получение первоначальных данных

Прежде чем приложение в первый раз получит изменения, оно должно выполнить запрос для получения первоначальных данных и версии синхронизации. Приложение должно получить соответствующие данные напрямую из таблицы, а затем вызвать функцию CHANGE_TRACKING_CURRENT_VERSION() для получения первоначальной версии. Эта версия передается функции CHANGETABLE(CHANGES …) при первом получении изменений.

В следующем примере показано получение первоначальной версии синхронизации и первоначального набора данных.

    -- Obtain the current synchronization version. This will be used next time that changes are obtained.
    SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

    -- Obtain initial data set.
    SELECT
        P.ProductID, P.Name, P.ListPrice
    FROM
        SalesLT.Product AS P

Использование функций отслеживания изменений для получения изменений

Чтобы получить измененные строки таблицы и сведения об этих изменениях, используется функция CHANGETABLE(CHANGES…). Например, следующий запрос получает изменения в таблице SalesLT.Product.

SELECT
    CT.ProductID, CT.SYS_CHANGE_OPERATION,
    CT.SYS_CHANGE_COLUMNS, CT.SYS_CHANGE_CONTEXT
FROM
    CHANGETABLE(CHANGES SalesLT.Product, @last_synchronization_version) AS CT

Как правило, клиенту нужно получить последние данные строки, а не просто первичные ключи. Поэтому приложение соединяет результаты запроса функции CHANGETABLE(CHANGES …) с данными в пользовательской таблице. Например, следующий запрос соединяется с таблицей SalesLT.Product, чтобы получить значения столбцов Name и ListPrice. Обратите внимание на использование OUTER JOIN. Это требуется, чтобы убедиться, что возвращаются сведения об изменениях в строках, удаленных из пользовательской таблицы.

SELECT
    CT.ProductID, P.Name, P.ListPrice,
    CT.SYS_CHANGE_OPERATION, CT.SYS_CHANGE_COLUMNS,
    CT.SYS_CHANGE_CONTEXT
FROM
    SalesLT.Product AS P
RIGHT OUTER JOIN
    CHANGETABLE(CHANGES SalesLT.Product, @last_synchronization_version) AS CT
ON
    P.ProductID = CT.ProductID

Чтобы получить версию, которая будет использоваться в следующем перечислении изменений, используется функция CHANGE_TRACKING_CURRENT_VERSION(), как показано в следующем примере.

SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION()

Когда приложение получает изменения, оно должно использовать как функцию CHANGETABLE(CHANGES…), так и CHANGE_TRACKING_CURRENT_VERSION(), как показано в следующем примере.

-- Obtain the current synchronization version. This will be used the next time CHANGETABLE(CHANGES...) is called.
SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

-- Obtain incremental changes by using the synchronization version obtained the last time the data was synchronized.
SELECT
    CT.ProductID, P.Name, P.ListPrice,
    CT.SYS_CHANGE_OPERATION, CT.SYS_CHANGE_COLUMNS,
    CT.SYS_CHANGE_CONTEXT
FROM
    SalesLT.Product AS P
RIGHT OUTER JOIN
    CHANGETABLE(CHANGES SalesLT.Product, @last_synchronization_version) AS CT
ON
    P.ProductID = CT.ProductID

Номера версий

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

Проверка последней синхронизированной версии

Сведения об изменениях хранятся ограниченное время. Длительность этого времени управляется параметром CHANGE_RETENTION, который указывается в инструкции ALTER DATABASE.

Имейте в виду, что время, заданное параметром CHANGE_RETENTION, определяет, как часто все приложения должны запрашивать изменения в базе данных. Если значение параметра last_synchronization_version в приложении старше минимально допустимой версии синхронизации для таблицы, это приложение не сможет выполнить достоверное перечисление изменений. Это объясняется тем, что некоторые данные изменений могли быть очищены. Прежде чем приложение получит изменения с помощью функции CHANGETABLE(CHANGES …), оно должно проверить значение параметра last_synchronization_version, которое нужно передать функции CHANGETABLE(CHANGES …). Если значение параметра last_synchronization_version недопустимо, приложение должно повторно инициализировать все данные.

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

    -- Check individual table.
    IF (@last_synchronization_version < CHANGE_TRACKING_MIN_VALID_VERSION(
                                       OBJECT_ID('SalesLT.Product')))
    BEGIN
      -- Handle invalid version and do not enumerate changes.
      -- Client must be reinitialized.
    END

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

    -- Check all tables with change tracking enabled
    IF EXISTS (
      SELECT * FROM sys.change_tracking_tables
      WHERE min_valid_version > @last_synchronization_version )
    BEGIN
      -- Handle invalid version & do not enumerate changes
      -- Client must be reinitialized
    END

Использование отслеживания столбцов

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

Сведения об отслеживании столбцов отображаются в столбце SYS_CHANGE_COLUMNS, который возвращается функцией CHANGETABLE(CHANGES …).

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

В следующем примере столбец CT_ThumbnailPhoto будет иметь значение NULL, если он не был изменен. Этот столбец может также принимать значение NULL, потому что ему оно присвоено, поэтому приложение может использовать столбец CT_ThumbNailPhoto_Changed, чтобы определить, происходили ли изменения.

DECLARE @PhotoColumnId int = COLUMNPROPERTY(
    OBJECT_ID('SalesLT.Product'),'ThumbNailPhoto', 'ColumnId')

SELECT
    CT.ProductID, P.Name, P.ListPrice, -- Always obtain values.
    CASE
           WHEN CHANGE_TRACKING_IS_COLUMN_IN_MASK(
                     @PhotoColumnId, CT.SYS_CHANGE_COLUMNS) = 1
            THEN ThumbNailPhoto
            ELSE NULL
      END AS CT_ThumbNailPhoto,
      CHANGE_TRACKING_IS_COLUMN_IN_MASK(
                     @PhotoColumnId, CT.SYS_CHANGE_COLUMNS) AS
                                   CT_ThumbNailPhoto_Changed
     CT.SYS_CHANGE_OPERATION, CT.SYS_CHANGE_COLUMNS,
     CT.SYS_CHANGE_CONTEXT
FROM
     SalesLT.Product AS P
INNER JOIN
     CHANGETABLE(CHANGES SalesLT.Product, @last_synchronization_version) AS CT
ON
     P.ProductID = CT.ProductID AND
     CT.SYS_CHANGE_OPERATION = 'U'

Получение согласованных и правильных результатов

Для получения измененных данных для таблицы требуется несколько шагов. Имейте в виду, что, если не удастся решить некоторые проблемы, будут возращены несогласованные или неверные результаты.

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

  1. Проверить последнюю синхронизированную версию с помощью функции CHANGE_TRACKING_MIN_VALID_VERSION().

  2. Получить версию, которая будет использована для получения изменений в следующий раз, с помощью функции CHANGE_TRACKING_CURRENT_VERSION().

  3. Получите изменения для таблицы Sales с помощью функции CHANGETABLE(CHANGES …).

  4. Получите изменения для таблицы SalesOrders с помощью функции CHANGETABLE(CHANGES …).

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

  • Процесс очистки выполняется в фоновом режиме и удаляет данные отслеживания изменений старше указанного срока хранения.

    Процесс очистки является отдельным фоновым процессом и руководствуется сроком хранения, заданным при настройке отслеживания изменений в базе данных. Проблема заключается в том, что он может выполняться в промежуток между проверкой последней версии синхронизации и вызовом функции CHANGETABLE(CHANGES…). Последняя достоверная версия синхронизации может оказаться ошибочной ко времени получения изменений. Поэтому функция может возвратить неверные результаты.

  • В таблицах Sales и SalesOrders постоянно выполняются операции DML, например следующие.

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

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

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

Использование изоляции моментального снимка

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

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

  1. Установите уровень изоляции транзакции на уровень моментальных снимков и запустите транзакцию.

  2. Проверьте последнюю версию синхронизации с помощью функции CHANGE_TRACKING_MIN_VALID_VERSION().

  3. Получите версию, которая будет использоваться в следующий раз, с помощью функции CHANGE_TRACKING_CURRENT_VERSION().

  4. Получите изменения для таблицы Sales с помощью функции CHANGETABLE(CHANGES …).

  5. Получите изменения для таблицы Salesorders с помощью функции CHANGETABLE(CHANGES …).

  6. Зафиксируйте транзакцию.

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

  • Если очистка происходит после проверки последней версии синхронизации, результаты функции CHANGETABLE(CHANGES …) будут, тем не менее, достоверными, так как операция удаления, выполняемая процессом очистки, не будет видна внутри транзакции.

  • Все изменения в таблице Sales или SalesOrders после получения версии следующей синхронизации не будут видны в транзакции, поэтому вызовы функции CHANGETABLE(CHANGES …) никогда не будут возвращать изменения с версией более поздней, чем возвращенные функцией CHANGE_TRACKING_CURRENT_VERSION(). Сохранится также согласованность между таблицами Sales и SalesOrders, поскольку транзакции, зафиксированные между вызовами функции CHANGETABLE(CHANGES …), будут невидимы.

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

-- The database must be configured to enable snapshot isolation.
ALTER DATABASE AdventureWorksLT2008
    SET ALLOW_SNAPSHOT_ISOLATION ON;

Транзакция моментального снимка используется следующим образом.

SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRAN
  -- Verify that version of the previous synchronization is valid.
  -- Obtain the version to use next time.
  -- Obtain changes.
COMMIT TRAN

Дополнительные сведения о транзакции моментальных снимков см. в разделе Использование уровней изоляции строк на основе управления версиями.

Альтернативы для изоляции моментального снимка

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

  1. Проверяйте last_synchronization_version после вызовов функции CHANGETABLE().

  2. Проверяйте last_synchronization_version в каждом запросе, чтобы получить изменения с помощью функции CHANGETABLE().

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

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

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

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

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

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

ПримечаниеПримечание

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

Журнал изменений

Обновленное содержимое

Исправлена опечатка в образце кода для проверки достоверности last_synchronization_version для всех таблиц в базе данных.