Delen via


Werken met wijzigingen bijhouden (SQL Server)

van toepassing op:SQL ServerAzure SQL DatabaseAzure SQL Managed Instance

Toepassingen die wijzigingen bijhouden gebruiken, moeten bijgehouden wijzigingen kunnen verkrijgen, deze wijzigingen toepassen op een ander gegevensarchief en de brondatabase bijwerken. In dit artikel wordt beschreven hoe u deze taken uitvoert, evenals de rol die het bijhouden van wijzigingen speelt wanneer een failover optreedt en een database moet worden hersteld vanuit een back-up.

Wijzigingen verkrijgen met behulp van functies voor het bijhouden van wijzigingen

Hierin wordt beschreven hoe u de functies voor het bijhouden van wijzigingen gebruikt om wijzigingen en informatie te verkrijgen over de wijzigingen die zijn aangebracht in een database.

Over de functies voor het bijhouden van wijzigingen

Toepassingen kunnen de volgende functies gebruiken om de wijzigingen te verkrijgen die zijn aangebracht in een database en informatie over de wijzigingen:

CHANGETABLE(CHANGES ...) Functie
Deze rijsetfunctie wordt gebruikt om query's uit te voeren op wijzigingsgegevens. De functie voert een query uit op de gegevens die zijn opgeslagen in de interne tabellen voor wijzigingen bijhouden. De functie retourneert een resultatenset die de primaire sleutels bevat van rijen die samen met andere wijzigingsgegevens zijn gewijzigd, zoals de bewerking, de bijgewerkte kolommen en de versie van de rij.

CHANGETABLE(CHANGES ...) gebruikt een laatste synchronisatieversie als argument. De laatste synchronisatieversie wordt verkregen met behulp van de variabele @last_synchronization_version. De semantiek van de laatste synchronisatieversie is als volgt:

  • De aanroepende client heeft wijzigingen verkregen en weet over alle wijzigingen tot en met de laatste synchronisatieversie.

  • CHANGETABLE(CHANGES ...) retourneert daarom alle wijzigingen die zijn opgetreden na de laatste synchronisatieversie.

    In de volgende afbeelding ziet u hoe CHANGETABLE(CHANGES ...) wordt gebruikt om wijzigingen te verkrijgen.

    diagram met een voorbeeld van de uitvoer van query's voor het bijhouden van wijzigingen.

    In dit voorbeeld is client A voor het laatst gesynchroniseerd om 9:30 uur, terwijl client B voor het laatst om 10:30 uur is gesynchroniseerd. Om 10:00 uur en opnieuw om 11:00 uur zijn er verschillende wijzigingen aangebracht in de gegevens. Deze bijgehouden wijzigingen worden hieronder samengevat.

    CHANGETABLE(CHANGES...) Uitvoer - 11:30 uur

    Client A laatst gesynchroniseerd om 9:30 uur.

    Product-ID Operatie Kolommen
    139 Bijwerken Naam, prijs
    140 Verwijderen -
    141 Invoegen -

    Client B laatst gesynchroniseerd om 10:30 uur.

    product-ID Operatie Kolommen
    139 Bijwerken Prijs
    140 Verwijderen -
    141 Update Prijs

De functie CHANGE_TRACKING_CURRENT_VERSION()
Wordt gebruikt om de huidige versie te verkrijgen die de volgende keer wordt gebruikt bij het uitvoeren van query's op wijzigingen. Deze versie vertegenwoordigt de versie van de laatste doorgevoerde transactie.

CHANGE_TRACKING_MIN_VALID_VERSION() functie
Wordt gebruikt om de minimaal geldige versie te verkrijgen die een client kan hebben en nog steeds geldige resultaten kan verkrijgen van CHANGETABLE(). De client moet de laatste synchronisatieversie controleren op basis van de waarde die door deze functie wordt geretourneerd. Als de laatste synchronisatieversie kleiner is dan de versie die door deze functie wordt geretourneerd, kan de client geen geldige resultaten verkrijgen van CHANGETABLE() en moet deze opnieuw initialiseren.

Initiële gegevens verkrijgen

Voordat een toepassing voor het eerst wijzigingen kan verkrijgen, moet de toepassing een query verzenden om de initiële gegevens en de synchronisatieversie te verkrijgen. De toepassing moet de juiste gegevens rechtstreeks uit de tabel ophalen en vervolgens CHANGE_TRACKING_CURRENT_VERSION() gebruiken om de eerste versie te verkrijgen. Deze versie wordt doorgegeven aan CHANGETABLE(CHANGES ...) de eerste keer dat wijzigingen worden verkregen.

In het volgende voorbeeld ziet u hoe u de eerste synchronisatieversie en de initiële gegevensset kunt verkrijgen.

declare @synchronization_version bigint;

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

De functies voor het bijhouden van wijzigingen gebruiken om wijzigingen te verkrijgen

Als u de gewijzigde rijen voor een tabel en informatie over de wijzigingen wilt ophalen, gebruikt u CHANGETABLE(CHANGES...). Met de volgende query worden bijvoorbeeld wijzigingen opgehaald voor de SalesLT.Product tabel.

declare @last_synchronization_version bigint;

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;

Normaal gesproken wil een client de meest recente gegevens voor een rij verkrijgen in plaats van alleen de primaire sleutels voor de rij. Daarom zou een applicatie de resultaten van CHANGETABLE(CHANGES ...) samenvoegen met de gegevens in de gebruikerstabel. De volgende query wordt bijvoorbeeld samengevoegd met de SalesLT.Product tabel om de waarden voor de kolommen Name en ListPrice op te halen. Let op het gebruik van OUTER JOIN. Dit is vereist om ervoor te zorgen dat de wijzigingsgegevens worden geretourneerd voor de rijen die zijn verwijderd uit de gebruikerstabel.

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;

Gebruik CHANGE_TRACKING_CURRENT_VERSION(), zoals wordt weergegeven in het volgende voorbeeld om de versie te verkrijgen voor gebruik in de volgende opsomming.

SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

Wanneer een toepassing wijzigingen verkrijgt, moet deze zowel CHANGETABLE(CHANGES...) als CHANGE_TRACKING_CURRENT_VERSION() gebruiken, zoals wordt weergegeven in het volgende voorbeeld.

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

Versienummers

Een database met wijzigingstracering ingeschakeld heeft een versieteller die toeneemt naarmate er wijzigingen worden aangebracht in de bijgehouden tabellen. Aan elke gewijzigde rij is een versienummer gekoppeld. Wanneer een aanvraag naar een toepassing wordt verzonden om te zoeken naar wijzigingen, wordt een functie aangeroepen die een versienummer levert. De functie retourneert informatie over alle wijzigingen die zijn aangebracht sinds die versie. Op sommige manieren is de versie voor het bijhouden van wijzigingen vergelijkbaar met de rowversion gegevenstype.

De laatst gesynchroniseerde versie valideren

Informatie over wijzigingen wordt gedurende een beperkte periode bijgehouden. De tijdsduur wordt bepaald door de CHANGE_RETENTION parameter die kan worden opgegeven als onderdeel van de ALTER DATABASE.

De opgegeven tijd voor CHANGE_RETENTION bepaalt hoe vaak alle toepassingen wijzigingen uit de database moeten aanvragen. Als een toepassing een waarde heeft voor last_synchronization_version die ouder is dan de minimaal geldige synchronisatieversie voor een tabel, kan die toepassing geen geldige opsomming voor wijzigingen uitvoeren. Dit komt doordat bepaalde wijzigingsgegevens mogelijk zijn opgeschoond. Voordat een toepassing wijzigingen verkrijgt met BEHULP van CHANGETABLE (CHANGES...), moet de toepassing de waarde valideren voor last_synchronization_version dat deze van plan is door te geven aan CHANGETABLE(CHANGES...). Als de waarde van last_synchronization_version niet geldig is, moet die toepassing alle gegevens opnieuw initialiseren.

In het volgende voorbeeld ziet u hoe u de geldigheid van de waarde van last_synchronization_version voor elke tabel kunt controleren.

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

Zoals in het volgende voorbeeld wordt weergegeven, kan de geldigheid van de waarde van last_synchronization_version worden gecontroleerd op alle tabellen in de database.

-- Check all tables with change tracking enabled
IF EXISTS (
  SELECT 1 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;

Kolomtracering gebruiken

Met het bijhouden van kolommen kunnen toepassingen alleen de gegevens verkrijgen voor de kolommen die zijn gewijzigd in plaats van de hele rij. Denk bijvoorbeeld aan het scenario waarin een tabel een of meer kolommen bevat die groot zijn, maar zelden veranderen; en heeft ook andere kolommen die vaak veranderen. Zonder kolomtracking kan een toepassing alleen bepalen dat een rij is gewijzigd en alle gegevens die de grote kolomgegevens bevatten, moeten synchroniseren. Met behulp van kolomtracering kan een toepassing echter bepalen of de grote kolomgegevens zijn gewijzigd en alleen de gegevens synchroniseren als deze zijn gewijzigd.

Informatie over het bijhouden van kolommen wordt weergegeven in de SYS_CHANGE_COLUMNS kolom die wordt geretourneerd door de functie CHANGETABLE(CHANGES ...) .

Kolomtracking kan worden gebruikt zodat NULL wordt geretourneerd voor een kolom die niet is gewijzigd. Als de kolom kan worden gewijzigd in NULL, moet er een afzonderlijke kolom worden geretourneerd om aan te geven of de kolom is gewijzigd.

In het volgende voorbeeld zal de kolom CT_ThumbnailPhotoNULL blijven als die kolom niet is gewijzigd. Deze kolom kan ook worden NULL omdat deze is gewijzigd in NULL. De toepassing kan de CT_ThumbNailPhoto_Changed kolom gebruiken om te bepalen of de kolom is gewijzigd.

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

Consistente en juiste resultaten verkrijgen

Voor het verkrijgen van de gewijzigde gegevens voor een tabel zijn meerdere stappen vereist. Inconsistente of onjuiste resultaten kunnen worden geretourneerd als bepaalde problemen niet worden overwogen en afgehandeld.

Als u bijvoorbeeld de wijzigingen wilt verkrijgen die zijn aangebracht in een Sales tabel en SalesOrders tabel, voert een toepassing de volgende stappen uit:

  1. Valideer de laatst gesynchroniseerde versie met behulp van CHANGE_TRACKING_MIN_VALID_VERSION().

  2. Haal de versie op die kan worden gebruikt om de volgende keer te wijzigen met behulp van CHANGE_TRACKING_CURRENT_VERSION().

  3. Haal de wijzigingen voor de Sales tabel op met behulp van CHANGETABLE(CHANGES...).

  4. Haal de wijzigingen voor de SalesOrders tabel op met behulp van CHANGETABLE(CHANGES...).

Er worden twee processen uitgevoerd in de database die van invloed kunnen zijn op de resultaten die worden geretourneerd door de vorige stappen:

  • Het opschoningsproces wordt op de achtergrond uitgevoerd en verwijdert informatie over het bijhouden van wijzigingen die ouder is dan de opgegeven bewaarperiode.

    Het opschoonproces is een afzonderlijk achtergrondproces dat gebruikmaakt van de bewaarperiode die wordt opgegeven wanneer u wijzigingen bijhouden voor de database configureert. Het probleem is dat het opschoonproces kan optreden in de tijd tussen het moment waarop de laatste synchronisatieversie is gevalideerd en wanneer de aanroep naar CHANGETABLE(CHANGES...) wordt gemaakt. Een laatste synchronisatieversie die geldig was, is mogelijk niet meer geldig op het moment dat de wijzigingen worden opgehaald. Daarom kunnen onjuiste resultaten worden geretourneerd.

  • Lopende DML-bewerkingen worden uitgevoerd in de tabellen Sales en SalesOrders, zoals de volgende bewerkingen:

    • Wijzigingen in de tabellen kunnen worden aangebracht nadat de versie voor de volgende keer is verkregen met behulp van CHANGE_TRACKING_CURRENT_VERSION(). Daarom kunnen er meer wijzigingen worden geretourneerd dan verwacht.

    • Een transactie kan worden doorgevoerd in de tijd tussen de aanroep om wijzigingen op te halen uit de Sales tabel en de aanroep om wijzigingen op te halen uit de SalesOrders tabel. Daarom kunnen de resultaten voor de SalesOrder tabel een foreign key waarde hebben die niet bestaat in de Sales tabel.

Om de eerder vermelde uitdagingen te overwinnen, raden we u aan om momentopname-isolatie te gebruiken. Dit helpt om consistentie van wijzigingsinformatie te garanderen en racevoorwaarden te voorkomen die betrekking hebben op de opschoontaak op de achtergrond. Als u geen momentopnametransacties gebruikt, kan het ontwikkelen van een toepassing die gebruikmaakt van wijzigingen bijhouden aanzienlijk meer inspanning vereisen.

Isolatie van momentopnamen gebruiken

Wijzigingen bijhouden is ontworpen om goed te werken met isolatie van momentopnamen. Isolatie van momentopnamen moet zijn ingeschakeld voor de database. Alle stappen die nodig zijn om wijzigingen te verkrijgen, moeten worden opgenomen in een momentopnametransactie. Dit zorgt ervoor dat alle wijzigingen die worden aangebracht in gegevens tijdens het verkrijgen van wijzigingen niet zichtbaar zijn voor de query's in de momentopnametransactie.

Voer de volgende stappen uit om gegevens in een momentopnametransactie te verkrijgen:

  1. Stel het niveau van transactieisolatie in op momentopnamen en start een transactie.

  2. Valideer de laatste synchronisatieversie met behulp van CHANGE_TRACKING_MIN_VALID_VERSION().

  3. Haal de versie op die de volgende keer moet worden gebruikt met behulp van CHANGE_TRACKING_CURRENT_VERSION().

  4. Haal de wijzigingen voor de Sales tabel op met behulp van CHANGETABLE(CHANGES ...)

  5. Haal de wijzigingen voor de SalesOrders tabel op met behulp van CHANGETABLE(CHANGES ...)

  6. Voer de transactie door.

Enkele punten die u moet onthouden omdat alle stappen voor het verkrijgen van wijzigingen zich in een momentopnametransactie bevinden:

  • Als opschoning plaatsvindt nadat de laatste synchronisatieversie is gevalideerd, zijn de resultaten van CHANGETABLE (CHANGES ...) nog steeds geldig omdat de verwijderingsbewerkingen die door opschoning worden uitgevoerd, niet zichtbaar zijn binnen de transactie.

  • Wijzigingen die zijn aangebracht in de Sales tabel of de SalesOrders tabel nadat de volgende synchronisatieversie is verkregen, zijn niet zichtbaar en de aanroepen naar CHANGETABLE(CHANGES ...) retourneren nooit wijzigingen met een versie die later is geretourneerd door CHANGE_TRACKING_CURRENT_VERSION(). Consistentie tussen de Sales tabel en de SalesOrders tabel wordt ook gehandhaafd, omdat de transacties die zijn vastgelegd in de tijd tussen aanroepen naar CHANGETABLE(CHANGES ...) niet zichtbaar zijn.

In het volgende voorbeeld ziet u hoe isolatie van momentopnamen is ingeschakeld voor een database.

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

Er wordt als volgt een momentopnametransactie gebruikt:

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

Voor meer informatie over momentopnametransacties, zie SET TRANSACTION ISOLATION LEVEL (Transact-SQL).

Isolatie van momentopnamen en opschonen

Als u zowel snapshotisolatie als wijzigingsopsporing inschakelt voor dezelfde database, of op twee verschillende databases binnen hetzelfde exemplaar, kan het opschoningsproces verlopen rijen in sys.syscommittab achterlaten wanneer er een open transactie is in de database met snapshotisolatie. Dit kan gebeuren omdat het opschoonproces voor het bijhouden van wijzigingen een instancebrede minimum watermerk (wat het veilige opschoonpunt is) in aanmerking neemt bij het uitvoeren van de opschoonbewerking. Dit wordt gedaan om ervoor te zorgen dat het automatische opschonen van het wijzigingen-bijhouden geen rijen verwijdert die mogelijk nodig zijn voor de open transactie in de database waarvoor isolatie van momentopnamen is ingeschakeld. Behoud vastgelegde isolatie van momentopnamen en isolatietransacties voor momentopnamen zo kort mogelijk om ervoor te zorgen dat verlopen rijen van sys.syscommittab tijdig worden opgeschoond.

Alternatieven voor isolatie van momentopnamen

Er zijn alternatieven voor het gebruik van isolatie van momentopnamen, maar er is meer werk nodig om ervoor te zorgen dat aan alle toepassingsvereisten wordt voldaan. Voer de volgende stappen uit om ervoor te zorgen dat de last_synchronization_version geldig is en gegevens niet worden verwijderd door het opschoonproces voordat wijzigingen worden verkregen:

  1. Controleer last_synchronization_version nadat de oproepen naar CHANGETABLE() zijn gedaan.

  2. Controleer last_synchronization_version als onderdeel van elke query om wijzigingen te verkrijgen met CHANGETABLE().

Wijzigingen kunnen optreden nadat de synchronisatieversie voor de volgende opsomming is verkregen. Er zijn twee manieren om deze situatie aan te pakken. De optie die wordt gebruikt, is afhankelijk van de toepassing en hoe deze de bijwerkingen van elke benadering kan afhandelen:

  • Negeer wijzigingen met een versie die groter is dan de nieuwe synchronisatieversie.

    Deze benadering heeft het neveneffect dat een nieuwe of bijgewerkte rij wordt overgeslagen als deze is gemaakt of bijgewerkt vóór de nieuwe synchronisatieversie, maar daarna wordt bijgewerkt. Als er een nieuwe rij is, kan er een probleem met referentiële integriteit optreden als er een rij in een andere tabel is die naar de overgeslagen rij verwijst. Als er een bestaande rij is die is bijgewerkt, wordt de rij overgeslagen en niet gesynchroniseerd tot de volgende keer.

  • Neem alle wijzigingen op, zelfs wijzigingen met een versie die groter is dan de nieuwe synchronisatieversie.

    De rijen met een versie die groter is dan de nieuwe synchronisatieversie, worden opnieuw verkregen bij de volgende synchronisatie. Dit moet worden verwacht en verwerkt door de toepassing.

Naast de vorige twee opties kunt u een benadering bedenken die beide opties combineert, afhankelijk van de bewerking. U wilt bijvoorbeeld een toepassing waarmee het het beste is om wijzigingen die nieuwer zijn dan de volgende synchronisatieversie, waarin de rij is gemaakt of verwijderd, te negeren, maar waarbij bijgewerkte rijen niet worden genegeerd.

Notitie

Voor het kiezen van de methode die voor de toepassing werkt wanneer u wijzigingen bijhouden (of een aangepast mechanisme voor bijhouden) gebruikt, is een belangrijke analyse vereist. Daarom is het veel eenvoudiger om momentopname-isolatie te gebruiken.

Hoe het bijhouden van wijzigingen veranderingen in een database afhandelt

Sommige toepassingen die gebruikmaken van het bijhouden van wijzigingen voeren tweerichtingssynchronisatie uit met een ander gegevensarchief. Dat wil gezegd dat wijzigingen die in de SQL Server-database worden aangebracht, worden bijgewerkt in het andere gegevensarchief en wijzigingen die in het andere archief worden aangebracht, worden bijgewerkt in de SQL Server-database.

Wanneer een toepassing de lokale database bijwerken met wijzigingen uit een ander gegevensarchief, moet de toepassing de volgende bewerkingen uitvoeren:

  • Controleer op conflicten.

    Er treedt een conflict op wanneer dezelfde gegevens tegelijkertijd worden gewijzigd in beide gegevensarchieven. De toepassing moet kunnen controleren op een conflict en voldoende informatie verkrijgen om het conflict op te lossen.

  • Sla informatie over toepassingscontext op.

    In de toepassing worden gegevens opgeslagen met de informatie over het bijhouden van wijzigingen. Deze informatie zou samen met andere informatie over het bijhouden van wijzigingen beschikbaar zijn wanneer wijzigingen zijn verkregen uit de lokale database. Een veelvoorkomend voorbeeld van deze contextuele informatie is een id voor het gegevensarchief dat de bron van de wijziging was.

Een synchronisatietoepassing kan de volgende functies gebruiken om de vorige bewerkingen uit te voeren:

  • CHANGETABLE(VERSION...)

    Wanneer een toepassing wijzigingen aanbrengt, kan deze functie worden gebruikt om te controleren op conflicten. De functie haalt de meest recente informatie over het bijhouden van wijzigingen op voor een opgegeven rij in een bijgehouden wijzigingstabel. De informatie over het bijhouden van wijzigingen bevat de versie van de rij die voor het laatst is gewijzigd. Met deze informatie kan een toepassing bepalen of de rij is gewijzigd na de laatste keer dat de toepassing is gesynchroniseerd.

  • (Only if applicable, otherwise the original translation stands)

    Een toepassing kan deze component gebruiken om contextgegevens op te slaan.

Controleren op conflicten

In een synchronisatiescenario in twee richtingen moet de clienttoepassing bepalen of een rij niet is bijgewerkt sinds de toepassing de wijzigingen voor het laatst heeft verkregen.

In het volgende voorbeeld ziet u hoe u de functie CHANGETABLE(VERSION ...) gebruikt om te controleren op conflicten op de meest efficiënte manier, zonder een afzonderlijke query. In het voorbeeld bepaalt CHANGETABLE(VERSION ...) de SYS_CHANGE_VERSION voor de rij die is opgegeven door @product id. CHANGETABLE(CHANGES ...) kan dezelfde informatie verkrijgen, maar dat zou minder efficiënt zijn. Als de waarde van SYS_CHANGE_VERSION voor de rij groter is dan de waarde van @last_sync_version, is er een conflict. Als er een conflict is, wordt de rij niet bijgewerkt. De ISNULL() controle is vereist omdat er mogelijk geen wijzigingsinformatie beschikbaar is voor de rij. Er zouden geen wijzigingsgegevens bestaan als de rij niet is bijgewerkt sinds het bijhouden van wijzigingen is ingeschakeld of omdat de wijzigingsgegevens zijn opgeschoond.

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

Met de volgende code kunt u het bijgewerkte aantal rijen controleren en meer informatie over het conflict identificeren.

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

Contextinformatie instellen

Met behulp van de WITH CHANGE_TRACKING_CONTEXT-clausule kan een toepassing contextinformatie samen met de wijzigingsinformatie opslaan. Deze informatie kan vervolgens worden verkregen uit de SYS_CHANGE_CONTEXT kolom die wordt geretourneerd door CHANGETABLE(CHANGES ...).

Contextinformatie wordt doorgaans gebruikt om de bron van de wijzigingen te identificeren. Als de bron van de wijziging kan worden geïdentificeerd, kan die informatie worden gebruikt door een gegevensarchief om te voorkomen dat er wijzigingen worden verkregen wanneer deze opnieuw wordt gesynchroniseerd.

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

Consistente en juiste resultaten garanderen

Een toepassing moet rekening houden met het opschoningsproces wanneer de waarde van @last_sync_versionwordt gevalideerd. Dit komt doordat gegevens kunnen zijn verwijderd nadat CHANGE_TRACKING_MIN_VALID_VERSION() is aangeroepen, maar voordat de update werd uitgevoerd.

U moet isolatie van momentopnamen gebruiken en de wijzigingen aanbrengen binnen een momentopnametransactie.

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

Notitie

Er is een mogelijkheid dat de rij die binnen de momentopnametransactie wordt bijgewerkt, in een andere transactie is bijgewerkt nadat de momentopnametransactie is gestart. In dit geval treedt er een updateconflict op voor de isolatie van momentopnamen en leidt dit ertoe dat de transactie wordt beëindigd. Als dit gebeurt, voert u de update opnieuw uit. Dit zal vervolgens resulteren in de detectie van een conflict bij het bijhouden van wijzigingen, waardoor er geen rijen worden gewijzigd.

Wijzigingen bijhouden en gegevens herstellen

Toepassingen waarvoor synchronisatie is vereist, moeten rekening houden met het geval waarin een database waarvoor wijzigingen bijhouden is ingeschakeld, wordt teruggezet naar een eerdere versie van de gegevens. Dit kan gebeuren nadat een database is hersteld vanuit een back-up, wanneer er een failover naar een asynchrone databasespiegel is, of wanneer er een fout optreedt bij het gebruik van logboekverzending. In het volgende scenario ziet u het probleem:

  1. Tabel T1 is voorzien van wijzigingstracering en de minimaal geldige versie voor de tabel is 50.

  2. Een clienttoepassing synchroniseert gegevens op versie 100 en verkrijgt informatie over alle wijzigingen tussen versie 50 en 100.

  3. Er worden extra wijzigingen aangebracht in tabel T1 na versie 100.

  4. In versie 120 is er een fout opgetreden en herstelt de databasebeheerder de database met gegevensverlies. Na de herstelbewerking bevat de tabel gegevens tot en met versie 70 en is de minimaal gesynchroniseerde versie nog steeds 50.

    Dit betekent dat het gesynchroniseerde gegevensarchief gegevens bevat die niet meer aanwezig zijn in het primaire gegevensarchief.

  5. T1 wordt vaak bijgewerkt. Hiermee wordt de huidige versie naar 130 verplaatst.

  6. De clienttoepassing wordt opnieuw gesynchroniseerd en levert een laatst gesynchroniseerde versie van 100. De client valideert dit getal omdat 100 groter is dan 50.

    De client verkrijgt wijzigingen tussen versie 100 en 130. Op dit moment is de client niet op de hoogte dat de wijzigingen tussen 70 en 100 niet hetzelfde zijn als voorheen. De gegevens op de client en server worden niet gesynchroniseerd.

Als de database is hersteld naar een punt na versie 100, zijn er geen problemen met synchronisatie. De client en server synchroniseren gegevens correct tijdens het volgende synchronisatie-interval.

Wijzigingen bijhouden biedt geen ondersteuning voor het herstellen van gegevensverlies. Er zijn echter twee opties voor het detecteren van deze typen synchronisatieproblemen:

  • Sla een databaseversie-id op de server op en werk deze waarde bij wanneer een database wordt hersteld of op een andere manier gegevens kwijtraakt. Elke clienttoepassing slaat de id op en elke client moet deze id valideren wanneer gegevens worden gesynchroniseerd. Als er gegevensverlies optreedt, komen de id's niet overeen en zullen de clients herinitialiseren. Een nadeel is dat als het gegevensverlies de laatste gesynchroniseerde grens niet heeft overschreden, de client onnodige herinitialisatie kan uitvoeren.

  • Wanneer een client een query uitvoert op wijzigingen, registreert u het laatste synchronisatieversienummer voor elke client op de server. Als er een probleem is met de gegevens, komen de laatst gesynchroniseerde versienummers niet overeen. Dit geeft aan dat een herinitialisatie is vereist.

Zie ook