Abrufen von Änderungen mit Änderungsnachverfolgungsfunktionen

In diesem Thema werden die Änderungsnachverfolgungsfunktionen in SQL Server 2008 beschrieben, und es wird erläutert, wie diese Funktionen verwendet werden, um die in einer Datenbank vorgenommenen Änderungen und Informationen zu diesen Änderungen abzurufen.

Informationen zu Änderungsnachverfolgungsfunktionen

Anwendungen können mit den folgenden Funktionen die in einer Datenbank vorgenommenen Änderungen sowie die Informationen zu diesen Änderungen abrufen:

  • CHANGETABLE(CHANGES …)-Funktion
    Diese Rowsetfunktion wird verwendet, um Änderungsinformationen abzufragen. Die Funktion fragt die in den internen Änderungsnachverfolgungstabellen gespeicherten Daten ab. Die Funktion gibt ein Resultset zurück, das die Primärschlüssel der Zeilen enthält, die sich geändert haben. Außerdem werden weitere Informationen zurückgegeben, z. B. der Vorgang, die aktualisierten Spalten und die Version der Zeile.

    CHANGETABLE(CHANGES …) verwendet die letzte Synchronisierungsversion als Argument. Die letzte Synchronisierungsversion wird wie in den Beispielen in diesem Thema dargestellt über die @last_synchronization_version-Variable abgerufen. Die Semantik der letzten Synchronisierungsversion lautet wie folgt:

    • Der aufrufende Client hat alle Änderungen bis zur letzten Synchronisierungsversion (einschließlich) abgerufen.

    • CHANGETABLE(CHANGES …) gibt also alle Änderungen zurück, die nach der letzten Synchronisierungsversion vorgenommen wurden.

      Die folgende Abbildung zeigt, wie CHANGETABLE (CHANGES.) verwendet wird, um Änderungen abzurufen.

      Beispiel einer Ausgabe einer Änderungsnachverfolgungs-Abfrage

  • CHANGE_TRACKING_CURRENT_VERSION()-Funktion
    Diese Funktion wird zum Abrufen der aktuellen Version verwendet. Diese wird das nächste Mal verwendet, wenn Änderungen abgerufen werden. Diese Version stellt die Version der letzten Transaktion dar, für die ein Commit ausgeführt wurde.

  • CHANGE_TRACKING_MIN_VALID_VERSION()-Funktion
    Diese Funktion wird verwendet, um die minimal gültige Version abzurufen, über die ein Client verfügen muss, damit CHANGETABLE() gültige Ergebnisse zurückgibt. Der Client muss die Version der letzten Synchronisierung mit dem Wert abgleichen, der von dieser Funktion zurückgegeben wird. Wenn die Version der letzten Synchronisierung niedriger ist als die von dieser Funktion zurückgegebene Version, kann der Client keine gültigen Ergebnisse von CHANGETABLE() abrufen und muss neu initialisiert werden.

Abrufen der Anfangsdaten

Damit eine Anwendung Änderungen abrufen kann, muss sie zunächst die Anfangsdaten und die Synchronisierungsversion abfragen. Die Anwendung muss die entsprechenden Daten direkt aus der Tabelle abrufen und dann CHANGE_TRACKING_CURRENT_VERSION() zum Abrufen der Anfangsversion verwenden. Diese Version wird beim ersten Abrufen von Änderungen an CHANGETABLE (CHANGES.) übergeben.

Das folgende Beispiel zeigt, wie die Anfangsversion der Synchronisierung und das Anfangsdataset abgerufen werden.

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

Verwenden der Änderungsnachverfolgungsfunktionen zum Abrufen von Änderungen

Verwenden Sie die Funktion CHANGETABLE(CHANGES…), um die geänderten Zeilen einer Tabelle und die zugehörigen Änderungsinformationen abzurufen. Beispielsweise werden mit der folgenden Abfrage die Änderungen für die SalesLT.Product-Tabelle abgerufen.

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

In der Regel möchte ein Client nicht nur die Primärschlüssel, sondern die neuesten Daten für eine Zeile abrufen. In diesem Fall führt eine Anwendung die Ergebnisse der CHANGETABLE(CHANGES …)-Funktion mit den Daten in der Benutzertabelle zusammen. Beispiel: Bei der folgenden Abfrage wird die Funktion mit der SalesLT.Product-Tabelle verknüpft, um die Werte der Name-Spalte und der ListPrice-Spalte abzurufen. Beachten Sie, dass OUTER JOIN verwendet wird. Dies ist erforderlich, um sicherzustellen, dass die Änderungsinformationen für die Zeilen zurückgegeben werden, die aus der Benutzertabelle gelöscht wurden.

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

Verwenden Sie die CHANGE_TRACKING_CURRENT_VERSION()-Funktion wie im folgenden Beispiel gezeigt, um die in der nächsten Änderungsenumeration zu verwendende Version abzurufen.

SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION()

Beim Abrufen von Änderungen muss eine Anwendung wie im folgenden Beispiel gezeigt sowohl CHANGETABLE(CHANGES...) als auch CHANGE_TRACKING_CURRENT_VERSION() verwenden.

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

Versionsnummern

Eine Datenbank mit aktivierter Änderungsnachverfolgung verfügt über einen Versionszähler, der hochgezählt wird, wenn Änderungen an nachverfolgten Tabellen vorgenommen werden. Jede geänderte Zeile verfügt über eine ihr zugeordnete Versionsnummer. Wenn eine Anforderung zur Abfrage von Änderungen an eine Anwendung gesendet wird, wird eine Funktion aufgerufen, die eine Versionsnummer angibt. Die Funktion gibt Informationen über alle Änderungen zurück, die ab dieser Version vorgenommen wurden. In gewisser Hinsicht entspricht die Änderungsnachverfolgungsversion dem Konzept des rowversion-Datentyps.

Überprüfen der letzten Synchronisierungsversion

Informationen über Änderungen werden für einen beschränkten Zeitraum beibehalten. Dieser Zeitraum wird mit dem CHANGE_RETENTION-Parameter festgelegt, der als Teil von ALTER DATABASE angegeben werden kann.

Beachten Sie, dass der mit CHANGE_RETENTION angegebene Zeitraum festlegt, wie häufig alle Anwendungen Änderungen von der Datenbank anfordern müssen. Wenn der Wert für last_synchronization_version einer Anwendung älter ist als die minimal gültige Synchronisierungsversion für eine Tabelle, kann diese Anwendung keine gültige Änderungsenumeration ausführen. Das liegt daran, dass einige Änderungsinformationen möglicherweise bereinigt wurden. Vor dem Abrufen von Änderungen mit CHANGETABLE(CHANGES …) muss eine Anwendung also den Wert von last_synchronization_version, der an CHANGETABLE(CHANGES …) übergeben werden soll, überprüfen. Wenn der Wert von last_synchronization_version nicht gültig ist, müssen alle Daten von der Anwendung neu initialisiert werden.

Im folgenden Beispiel wird gezeigt, wie die Gültigkeit des last_synchronization_version-Werts für die einzelnen Tabellen überprüft wird.

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

Wie im folgenden Beispiel gezeigt, kann die Gültigkeit des last_synchronization_version-Werts für alle Tabellen in der Datenbank überprüft werden.

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

Verwenden der Spaltennachverfolgung

Die Spaltennachverfolgung ermöglicht Anwendungen, Daten statt für die gesamte Zeile nur für die Spalten abzurufen, die geändert wurden. Nehmen Sie z. B. an, eine Tabelle hat ein oder mehrere große Spalten, in denen selten Änderungen vorgenommen werden, sowie andere Spalten, in denen häufig Änderungen vorgenommen werden. Ohne die Spaltennachverfolgung kann eine Anwendung nur die Änderung einer Zeile erkennen, sodass alle Daten, einschließlich der Daten in den großen Spalten, synchronisiert werden müssten. Mit der Spaltennachverfolgung kann eine Anwendung ermitteln, in welcher Spalte Daten geändert wurden, und nur die geänderten Daten synchronisieren.

Die Spaltennachverfolgungsinformationen sind in der SYS_CHANGE_COLUMNS-Spalte enthalten, die von der CHANGETABLE(CHANGES …)-Funktion zurückgegeben wird.

Die Spaltennachverfolgung kann so verwendet werden, dass NULL für Spalten ohne Änderungen zurückgegeben wird. Wenn die Spalte in NULL geändert werden kann, muss eine separate Spalte zurückgegeben werden, um anzugeben, ob die Spalte geändert wurde.

Im folgenden Beispiel wird für die Spalte CT_ThumbnailPhoto der Wert NULL zurückgegeben, wenn diese nicht geändert wurde. Der Wert dieser Spalte kann jedoch auch NULL lauten, da sie in NULL geändert werden kann. In diesem Fall kann die Anwendung mit der Spalte CT_ThumbNailPhoto_Changed angeben, ob die Spalte geändert wurde.

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'

Abrufen von konsistenten und richtigen Ergebnissen

Zum Abrufen der Änderungsdaten für eine Tabelle sind mehrere Schritte erforderlich. Beachten Sie, dass möglicherweise inkonsistente oder falsche Ergebnisse zurückgegeben werden, wenn bestimmte Probleme nicht berücksichtigt und behandelt werden.

Zum Abrufen der Änderungen in einer Tabelle mit dem Namen Sales und einer Tabelle mit dem Namen SalesOrders führt eine Anwendung z. B. die folgenden Schritte aus:

  1. Überprüfen Sie die letzte Synchronisierungsversion mit CHANGE_TRACKING_MIN_VALID_VERSION().

  2. Rufen Sie die Version, die beim nächsten Abruf von Änderungen verwendet werden kann, mit CHANGE_TRACKING_CURRENT_VERSION() ab.

  3. Rufen Sie die Änderungen für die Tabelle Sales mit CHANGETABLE(CHANGES …) ab.

  4. Rufen Sie die Änderungen für die Tabelle SalesOrders mit CHANGETABLE(CHANGES …) ab.

In der Datenbank werden zwei Prozesse ausgeführt, die sich auf die von den oben genannten Schritten zurückgegebenen Ergebnisse auswirken können:

  • Der im Hintergrund ausgeführte Cleanupprozess entfernt Änderungsnachverfolgungsinformationen, die älter sind als die angegebene Beibehaltungsdauer.

    Beim Cleanupprozess handelt es sich um einen eigenen, im Hintergrund ausgeführten Prozess, der die Beibehaltungsdauer verwendet, die bei der Konfiguration der Änderungsnachverfolgung für die Datenbank angegeben wurde. Das Problem liegt darin, dass der Cleanupprozess genau in dem Zeitraum nach der Überprüfung der letzten Synchronisierungsversion und vor dem Aufruf von CHANGETABLE(CHANGES…) ausgeführt werden kann. In diesem Fall kann es vorkommen, dass die gerade für gültig befundene letzte Synchronisierungsversion beim Abruf der Änderungen nicht mehr gültig ist. Aus diesem Grund kann es hier zu falschen Ergebnissen kommen.

  • In den Tabellen Sales und SalesOrders werden fortlaufende DML-Vorgänge ausgeführt, z. B.:

    • Mit der CHANGE_TRACKING_CURRENT_VERSION()-Funktion können Änderungen an den Tabellen vorgenommen werden, nachdem die Version für die nächste Aufzählung von Änderungen abgerufen wurde. In diesem Fall werden möglicherweise mehr Änderungen zurückgegeben als erwartet.

    • Im Zeitraum zwischen dem Aufruf der Funktion zum Abrufen der Änderungen in der Tabelle Sales und dem Aufruf der Funktion zum Abrufen der Änderungen in der Tabelle SalesOrders kann ein Commit für eine Transaktion ausgeführt werden. In diesem Fall enthalten die Ergebnisse für die Tabelle SalesOrder möglicherweise einen Fremdschlüsselwert, der in der Tabelle Sales nicht vorhanden ist.

Für die oben genannten Probleme wird die Verwendung der Momentaufnahmeisolation empfohlen. Hierdurch können Sie die Konsistenz der Änderungsinformationen sicherstellen und Racebedingungen im Zusammenhang mit dem im Hintergrund ausgeführten Cleanupprozess vermeiden. Ohne die Verwendung von Momentaufnahmetransaktionen ist die Entwicklung einer Anwendung, die die Änderungsnachverfolgung verwendet, mit erheblich mehr Aufwand verbunden.

Verwenden der Momentaufnahmeisolation

Die Änderungsnachverfolgung wurde für die Verwendung mit der Momentaufnahmeisolation optimiert. Die Momentaufnahmeisolation muss für die Datenbank aktiviert werden. Alle zum Abrufen von Änderungen erforderlichen Schritte müssen in eine Momentaufnahmetransaktion eingeschlossen werden. Hierdurch können Sie sicherstellen, dass die während des Abrufens von Änderungen an den Daten vorgenommenen Änderungen für die Abfragen in der Momentaufnahmetransaktion nicht sichtbar sind.

Führen Sie die folgenden Schritte aus, um Daten in einer Momentaufnahmetransaktion abzurufen:

  1. Legen Sie die Transaktionsisolationsstufe auf MOMENTAUFNAHME fest, und starten Sie die Transaktion.

  2. Überprüfen Sie die letzte Synchronisierungsversion mit CHANGE_TRACKING_MIN_VALID_VERSION().

  3. Rufen Sie die Version, die bei der nächsten Aufzählung von Änderungen verwendet wird, mit CHANGE_TRACKING_CURRENT_VERSION() ab.

  4. Rufen Sie die Änderungen für die Tabelle Sales mit CHANGETABLE(CHANGES …) ab.

  5. Rufen Sie die Änderungen für die Tabelle Salesorders mit CHANGETABLE(CHANGES …) ab.

  6. Führen Sie einen Commit für die Transaktion aus.

Folgendes ist zu beachten, wenn alle Schritte zum Abrufen von Änderungen innerhalb einer Momentaufnahmetransaktion erfolgen:

  • Wenn nach der Überprüfung der letzten Synchronisierungsversion eine Bereinigung durchgeführt wird, sind die Ergebnisse der CHANGETABLE(CHANGES …)-Funktion dennoch gültig, da die vom Cleanupprozess durchgeführten Löschvorgänge innerhalb der Transaktion nicht sichtbar sind.

  • Alle Änderungen, die nach dem Abrufen der nächsten Synchronisierungsversion an den Tabellen Sales oder SalesOrders vorgenommen werden, sind nicht sichtbar, und der Aufruf der CHANGETABLE(CHANGES …)-Funktion gibt keine Änderungen mit Versionen zurück, die neuer sind als die von der CHANGE_TRACKING_CURRENT_VERSION()-Funktion zurückgegebene Version. Die Konsistenz zwischen den Tabellen Sales und SalesOrders wird ebenfalls sichergestellt, da die Transaktionen, für die zwischen den Aufrufen der CHANGETABLE(CHANGES …)-Funktion ein Commit ausgeführt wird, nicht sichtbar sind.

Das folgende Beispiel zeigt, wie die Momentaufnahmeisolation für eine Datenbank aktiviert wird.

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

Eine Momentaufnahmetransaktion wird wie folgt verwendet:

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

Weitere Informationen zu Momentaufnahmetransaktionen finden Sie unter Verwenden von auf Zeilenversionsverwaltung basierenden Isolationsstufen.

Alternativen zur Verwendung der Momentaufnahmeisolation

Es gibt Alternativen zur Verwendung der Momentaufnahmeisolation, die jedoch einen größeren Aufwand erfordern, um sicherzustellen, dass alle Anwendungsanforderungen erfüllt werden. Gehen Sie wie folgt vor, um sicherzustellen, dass der last_synchronization_version-Wert gültig ist und dass vor dem Abrufen der Änderungen keine Daten durch den Cleanupprozess entfernt werden:

  1. Überprüfen Sie den last_synchronization_version-Wert nach den Aufrufen der CHANGETABLE()-Funktion.

  2. Überprüfen Sie den last_synchronization_version-Wert bei jeder Abfrage zum Abrufen von Änderungen mit CHANGETABLE().

Änderungen können nach dem Abrufen der Synchronisierungsversion für die nächste Aufzählung auftreten. Dieses Problem lässt sich auf zwei Arten lösen: Welche Option Sie verwenden, hängt von der Anwendung ab und wie diese die Nebeneffekte des jeweiligen Ansatzes handhabt:

  • Ignorieren Sie Änderungen, deren Version neuer ist als die neue Synchronisierungsversion.

    Dieser Ansatz hat den Nebeneffekt, dass eine neue oder aktualisierte Zeile übersprungen wird, wenn diese vor der neuen Synchronisierungsversion erstellt oder aktualisiert wurde und danach ebenfalls aktualisiert wird. Bei einer neuen Zeile kann ein Problem mit der referenziellen Integrität auftreten, wenn eine erstellte Zeile in einer anderen Tabelle auf die übersprungene Zeile verweist. Eine aktualisierte Zeile wird übersprungen und erst beim nächsten Mal synchronisiert.

  • Berücksichtigen Sie alle Änderungen, auch die, deren Version neuer ist als die neue Synchronisierungsversion.

    Die Zeilen, deren Version neuer ist als die neue Synchronisierungsversion, werden bei der nächsten Synchronisation erneut abgerufen. Dies muss von der Anwendung entsprechend berücksichtigt werden.

Zusätzlich zu den beiden oben genannten Optionen können Sie abhängig vom Vorgang einen Ansatz entwerfen, der beide Optionen kombiniert. Sie können beispielsweise eine Anwendung entwickeln, für die es am besten ist, dass Änderungen mit einer neueren Version als die nächste Synchronisierungsversion ignoriert werden, bei denen es sich um Erstellungs- oder Löschvorgänge handelt, Aktualisierungsvorgänge jedoch nicht ignoriert werden.

HinweisHinweis

Zur Auswahl der richtigen Methode, die bei Verwendung der Änderungsnachverfolgung (oder benutzerdefinierter Nachverfolgungsmechanismen) für die Anwendung funktioniert, sind umfangreiche Analysen erforderlich. Aus diesem Grund ist es viel einfacher, die Momentaufnahmeisolation zu verwenden.

Änderungsverlauf

Aktualisierter Inhalt

Ein Tippfehler im Beispielcode für die Überprüfung der Gültigkeit von last_synchronization_version für alle Tabellen in der Datenbank wurde korrigiert.