Condividi tramite


Gestione delle modifiche in un database da parte del rilevamento delle modifiche

Alcune applicazioni che utilizzano il rilevamento delle modifiche eseguono la sincronizzazione bidirezionale con un altro archivio dati. Questo significa che le modifiche apportate al database di SQL Server vengono aggiornate nell'altro archivio dati, mentre le modifiche apportate all'archivio vengono aggiornate nel database di SQL Server.

Quando aggiorna il database locale in base alle modifiche di un altro archivio dati, un'applicazione deve effettuare le operazioni seguenti:

  • Verificare la presenza di conflitti.

    Un conflitto si verifica quando gli stessi dati vengono modificati contemporaneamente in entrambi gli archivi dati. L'applicazione deve essere in grado di verificare la presenza di un conflitto e di ottenere informazioni sufficienti per consentirne la risoluzione.

  • Archiviare le informazioni sul contesto dell'applicazione.

    L'applicazione archivia i dati che dispongono di informazioni sul rilevamento delle modifiche. Dopo che le modifiche sono state ottenute dal database locale, tali informazioni saranno disponibili con altre informazioni sul rilevamento delle modifiche. Un esempio comune di tali informazioni contestuali è rappresentato da un identificatore per l'archivio dati che costituiva l'origine della modifica.

Per eseguire le operazioni precedenti, un'applicazione di sincronizzazione può utilizzare le funzioni seguenti:

  • CHANGETABLE(VERSION…)

    Quando un'applicazione apporta modifiche, può utilizzare questa funzione per verificare la presenza di conflitti. La funzione ottiene le informazioni sul rilevamento delle modifiche più recenti per una riga specificata in una tabella con rilevamento. In tali informazioni è inclusa la versione della riga in cui è stata eseguita l'ultima modifica. Questa informazione consente a un'applicazione di determinare se la riga è stata modificata dopo l'ultima sincronizzazione dell'applicazione stessa.

  • WITH CHANGE_TRACKING_CONTEXT

    Un'applicazione può utilizzare questa clausola per archiviare dati relativi al contesto.

Verifica della presenza di conflitti

In uno scenario di sincronizzazione bidirezionale l'applicazione client deve determinare se una riga non è stata aggiornata dall'ultima volta in cui l'applicazione ha ottenuto le modifiche.

Nell'esempio seguente viene illustrato come utilizzare la funzione CHANGETABLE(VERSION ...) per verificare la presenza di conflitti nel modo più efficiente, senza eseguire una query separata. Nell'esempio la funzione CHANGETABLE(VERSION …) determina il valore di SYS_CHANGE_VERSION per la riga specificata da @product id. La stessa informazione può essere ottenuta mediante CHANGETABLE(CHANGES …), ma in modo meno efficiente. Se il valore di SYS_CHANGE_VERSION per la riga è maggiore del valore di @last\_sync\_version, si verifica un conflitto. In questo caso la riga non verrà aggiornata. Il controllo ISNULL() è necessario perché per la riga potrebbero non essere disponibili informazioni sulle modifiche. Nel caso in cui la riga non sia stata aggiornata dal momento in cui è stato abilitato il rilevamento delle modifiche o dal momento in cui le informazioni sulle modifiche sono state eliminate, non sarebbe infatti disponibile alcuna informazione sulle modifiche.

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

Il codice seguente consente di controllare il conteggio delle righe aggiornate e di identificare ulteriori informazioni sul conflitto.

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

Impostazione delle informazioni sul contesto

Utilizzando la clausola WITH CHANGE_TRACKING_CONTEXT un'applicazione può archiviare le informazioni sul contesto con le informazioni sulle modifiche. Tali informazioni possono quindi essere ottenute dalla colonna SYS_CHANGE_CONTEXT restituita da CHANGETABLE(CHANGES ...).

Le informazioni sul contesto vengono utilizzate in genere per identificare l'origine delle modifiche. Se è possibile identificare l'origine della modifica, tali informazioni possono essere utilizzate da un archivio dati per evitare di ottenere modifiche alla successiva sincronizzazione.

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

Come garantire risultati coerenti e corretti

Al momento della convalida del valore di @last\_sync\_version, è necessario che in un'applicazione venga considerato il processo di pulizia poiché i dati potrebbero essere stati rimossi i dati dopo la chiamata a CHANGE_TRACKING_MIN_VALID_VERSION (), ma prima che sia stato eseguito l'aggiornamento.

Nota importanteImportante

È consigliabile utilizzare l'isolamento dello snapshot e apportare le modifiche all'interno di una transazione snapshot.

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

[!NOTA]

È possibile che la riga in fase di aggiornamento all'interno della transazione snapshot sia stata aggiornata in un'altra transazione dopo l'avvio della transazione stessa. In questo caso, si verifica un conflitto nell'aggiornamento dell'isolamento dello snapshot e la transazione verrà terminata. Se si verifica questa situazione, riprovare a eseguire l'aggiornamento. In questo modo verrà individuato un conflitto nel rilevamento delle modifiche e nessuna riga verrà modificata.