Trabalhar com controle de alterações (SQL Server)

Aplica-se a: SQL Server (todas as versões com suporte) SQL do Azure Banco de Dados

Os aplicativos que usam o controle de alterações devem ser capazes de obter as alterações controladas, aplicá-las a outro repositório de dados e atualizar o banco de dados de origem. Este artigo descreve como executar essas tarefas e também a função que o controle de alterações desempenha quando ocorre um failover e um banco de dados deve ser restaurado de um backup.

Obter alterações usando as funções de controle de alterações

Descreve como usar as funções de controle de alterações para obter as alterações feitas em um banco de dados e as respectivas informações.

Sobre as funções de controle de alterações

Os aplicativos podem usar as funções a seguir para obter as alterações feitas em um banco de dados e as informações sobre as alterações:

Função CHANGETABLE(CHANGES ...)
Esta função de conjunto de linhas é usada para consultar informações de alteração. A função consulta os dados armazenados nas tabelas internas de controle de alterações e retorna um conjunto de resultados que contém as chaves primárias de linhas, que foram alteradas, junto com outras informações de alterações como a operação, as colunas atualizadas e a versão da linha.

A função CHANGETABLE(CHANGES ...) usa uma última versão de sincronização como um argumento. A última versão de sincronização é obtida por meio da variável @last_synchronization_version. A semântica da última versão de sincronização é a seguinte:

  • O cliente de chamada obteve e tem conhecimento de todas as alterações até, e inclusive, a última versão de sincronização.

  • A função CHANGETABLE(CHANGES ...) retornará todas as alterações ocorridas depois da última versão de sincronização.

    A ilustração a seguir mostra como a função CHANGETABLE(CHANGES ...) é usada para obter alterações.

    Diagrama que mostra um exemplo de saída de consulta de controle de alterações.

    Neste exemplo, o Cliente A foi sincronizado pela última vez às 9h30, enquanto o cliente B foi sincronizado pela última vez às 10h30. Às 10h e novamente às 11h, várias alterações foram feitas nos dados. Essas alterações controladas são resumidas abaixo.

    CHANGETABLE(CHANGES...) Saída – 11h30

    Cliente A última sincronização às 9h30.

    Produto ID Operação Colunas
    139 Atualizar Nome, Preço
    140 Excluir -
    141 Inserir -

    Cliente B sincronizado pela última vez às 10h30.

    Produto ID Operação Colunas
    139 Atualizar Preço
    140 Excluir -
    141 Atualizar Preço

Função CHANGE_TRACKING_CURRENT_VERSION()
Usado para obter a versão atual que será usada na próxima vez ao consultar alterações. Essa versão representa a versão da última transação confirmada.

Função CHANGE_TRACKING_MIN_VALID_VERSION()
Usado para obter a versão mínima válida que um cliente pode ter e ainda obter resultados válidos de CHANGETABLE(). O cliente deve comparar a última versão de sincronização com o valor retornado por essa função. Se a última versão de sincronização for menor que a versão retornada por essa função, o cliente não poderá obter os resultados válidos de CHANGETABLE() e deverá reinicializar os dados.

Obter dados iniciais

Antes que um aplicativo possa obter as alterações pela primeira vez, ele deverá enviar uma consulta para obter os dados iniciais e a versão de sincronização. O aplicativo deve obter os dados adequados diretamente da tabela e usar a função CHANGE_TRACKING_CURRENT_VERSION() para obter a versão inicial. Essa versão passará para CHANGETABLE(CHANGES ...) na primeira vez em que as alterações forem obtidas.

O exemplo a seguir mostra como obter a versão de sincronização inicial, bem como o conjunto de dados inicial.

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;

Usar as funções de controle de alterações para obter alterações

Para obter as linhas alteradas de uma tabela e as informações sobre as alterações, use a função CHANGETABLE(CHANGES...). Por exemplo, a consulta a seguir obtém as alterações da tabela SalesLT.Product .

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;

Geralmente, um cliente optará por obter os últimos dados de uma linha em vez de apenas as chaves primárias da linha. Além disso, um aplicativo juntaria os resultados de CHANGETABLE(CHANGES ...) com os dados na tabela do usuário. Por exemplo, a consulta a seguir une as linhas da tabela SalesLT.Product para obter os valores das colunas Name e ListPrice . Observe o uso da função OUTER JOIN. Ela é obrigatória para garantir que as informações de alteração sejam retornadas para essas linhas que foram excluídas da tabela do usuário.

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;

Para obter a versão a ser usada na próxima enumeração de alteração, use CHANGE_TRACKING_CURRENT_VERSION(), como mostra o exemplo a seguir.

SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION();

Quando um aplicativo obtiver alterações, ele deverá usar CHANGETABLE(CHANGES...) e CHANGE_TRACKING_CURRENT_VERSION(), como mostra o exemplo a seguir.

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

Números de versão

Um banco de dados com o controle de alterações habilitado tem um contador de versões que aumenta à medida que são realizadas mudanças nas tabelas controladas. Cada linha alterada tem um número de versão associado. Quando uma solicitação é enviada a um aplicativo para consultar as alterações, uma função é chamada para fornecer um número de versão. A função retorna informações sobre todas as alterações que foram feitas desde aquela versão. De certas formas, a versão de controle de alterações é semelhante no conceito ao tipo de dados rowversion .

Validar a última versão sincronizada

As informações sobre as alterações são mantidas por um período limitado. A duração desse tempo e controlada pelo parâmetro CHANGE_RETENTION que pode ser especificado como parte de ALTER DATABASE.

O tempo especificado para CHANGE_RETENTION determina com que frequência todos os aplicativos devem solicitar alterações do banco de dados. Se um aplicativo tiver um valor para last_synchronization_version mais antigo que a versão mínima de sincronização válida para uma tabela, esse aplicativo não poderá executar enumeração de alteração válida. Isso ocorre porque algumas informações de alteração podem ser apagadas. Antes que um aplicativo obtenha alterações usando CHANGETABLE(CHANGES ...), o aplicativo deve validar o valor para last_synchronization_version que planeja passar para CHANGETABLE(CHANGES ...). Se o valor de last_synchronization_version não for válido, esse aplicativo deverá reinicializar todos os dados.

O exemplo a seguir mostra como verificar a validade do valor de last_synchronization_version para cada tabela.

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

Como mostra o exemplo a seguir, a validade do valor de last_synchronization_version pode ser verificada em comparação a todas as tabelas no banco de dados.

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

Usar o acompanhamento de coluna

O rastreamento de coluna permite que os aplicativos obtenham dados apenas das colunas que foram alteradas em vez de toda a linha. Por exemplo, considere o cenário no qual uma tabela tem uma ou mais colunas extensas, mas que raramente têm alterações, e outras colunas com alterações frequentes. Com o rastreamento de coluna, um aplicativo pode determinar se apenas uma linha foi alterada e se seria preciso sincronizar todos os dados da coluna maior. Entretanto, por meio do rastreamento de coluna, um aplicativo poderá determinar se os dados da coluna grande foram alterados e sincronizar apenas os dados, caso tenham sido alterados.

As informações de acompanhamento de coluna são exibidas na coluna SYS_CHANGE_COLUMNS que é retornada pela função CHANGETABLE(CHANGES ...).

O acompanhamento de colunas pode ser usado para que NULL seja retornado para uma coluna que não foi alterada. Se a coluna puder ser alterada para NULL, outra coluna deverá ser retornada para indicar se a coluna foi alterada.

No exemplo a seguir, a CT_ThumbnailPhoto coluna será NULL se essa coluna não tiver sido alterada. Esta coluna também deve ser NULL porque ela foi alterada para NULL - o aplicativo pode usar a coluna CT_ThumbNailPhoto_Changed para determinar se a coluna foi alterada.

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

Obter resultados consistentes e corretos

Para obter os dados alterados de uma tabela é preciso seguir várias etapas. Resultados inconsistentes ou incorretos poderão ser retornados se determinados problemas não forem considerados e tratados.

Por exemplo, para obter as alterações feitas em uma Sales tabela e SalesOrders tabela, um aplicativo executaria as seguintes etapas:

  1. Validar a última versão sincronizada por meio de CHANGE_TRACKING_MIN_VALID_VERSION().

  2. Obter a versão que pode ser usada para obter a alteração na próxima vez por meio de CHANGE_TRACKING_CURRENT_VERSION().

  3. Obtenha as alterações da Sales tabela usando CHANGETABLE(CHANGES ...).

  4. Obtenha as alterações da SalesOrders tabela usando CHANGETABLE(CHANGES ...).

Dois processos estão ocorrendo no banco de dados que podem afetar os resultados retornados pelas etapas anteriores:

  • O processo de limpeza é executado em segundo plano e remove as informações de controle de alterações anteriores ao período de retenção especificado.

    A limpeza é um processo interno e em segundo plano que usa um período de retenção especificado quando você configura o controle de alterações para o banco de dados. O problema é que o processo de limpeza pode ocorrer na hora em que a última versão de sincronização foi validada e quando a chamada para CHANGETABLE(CHANGES...) for feita. Uma última versão de sincronização válida pode não ser mais válida quando as alterações forem recuperadas. Portanto, talvez sejam retornados resultados incorretos.

  • Operações DML em andamento estão ocorrendo nas tabelas e SalesOrders vendas, como as seguintes operações:

    • Alterações poderão ser feitas na tabela depois que a versão tiver sido obtida por meio de CHANGE_TRACKING_CURRENT_VERSION(). Talvez sejam retornadas mais alterações do que o esperado.

    • Uma transação pode ser confirmada no tempo entre a chamada para buscar alterações da Sales tabela e a chamada para buscar alterações da SalesOrders tabela. Portanto, os resultados da SalesOrder tabela podem ter um valor de chave estrangeira que não existe na Sales tabela.

Para superar os desafios previamente listados, é recomendável usar o isolamento de instantâneo. Isso ajudará a garantir a consistência das informações de alterações e evitará situações de competição relacionadas à tarefa de limpeza em segundo plano. Se você não usar transações de instantâneo, o desenvolvimento de um aplicativo que usa o controle de alterações poderá exigir significativamente mais esforço.

Usar o isolamento de instantâneo

O controle de alterações foi projetado para funcionar bem com o isolamento de instantâneo. O isolamento de instantâneo deve estar habilitado para o banco de dados. Todas as etapas são obrigatórias para obter as alterações que devem estar incluídas em uma transação de instantâneo. Isso garantirá que todas as alterações feitas nos dados durante a obtenção de alterações não fiquem visíveis para as consultas dentro da transação de instantâneo.

Para obter dados dentro de uma transação de instantâneo, execute as seguintes etapas:

  1. Defina o nível de isolamento da transação para o instantâneo e inicie uma transação.

  2. Valide a última versão de sincronização usando a função CHANGE_TRACKING_MIN_VALID_VERSION().

  3. Obtenha a versão a ser usada na próxima vez por meio de CHANGE_TRACKING_CURRENT_VERSION().

  4. Obtenha as alterações da Sales tabela usando CHANGETABLE(CHANGES ...)

  5. Obtenha as alterações da SalesOrders tabela usando CHANGETABLE(CHANGES ...)

  6. Confirme a transação.

Alguns pontos a serem lembrados, uma vez que todas as etapas para obter alterações estão em uma transação de instantâneo:

  • Se a limpeza ocorrer depois que a última versão de sincronização for validada, os resultados de CHANGETABLE(CHANGES ...) ainda serão válidos, pois as operações de exclusão executadas pela limpeza não ficarão visíveis dentro da transação.

  • Todas as alterações feitas na Sales tabela ou na SalesOrders tabela após a próxima versão de sincronização serem obtidas não ficarão visíveis e as chamadas para CHANGETABLE(CHANGES ...) nunca retornarão alterações com uma versão posterior à retornada por CHANGE_TRACKING_CURRENT_VERSION(). A consistência entre a Sales tabela e a SalesOrders tabela também será mantida, pois as transações que foram confirmadas no tempo entre chamadas para CHANGETABLE(CHANGES ...) não estarão visíveis.

O exemplo a seguir mostra como o isolamento de instantâneo é habilitado para um banco de dados.

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

Uma transação de instantâneo é usada a seguinte forma:

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

Para obter mais informações sobre transações de instantâneo, consulte SET TRANSACTION ISOLATION LEVEL (Transact-SQL).

Limpeza e isolamento de instantâneo

Habilitar o isolamento de instantâneo e o controle de alterações no mesmo banco de dados ou em dois bancos de dados diferentes dentro da mesma instância pode resultar no processo de limpeza deixando linhas sys.syscommittab expiradas quando há uma transação aberta no banco de dados com isolamento de instantâneo. Isso pode acontecer porque o processo de limpeza do controle de alterações leva em consideração uma marca-d'água baixa em toda a instância (que é a versão de limpeza segura) ao executar a limpeza. Isso é feito para garantir que o processo de limpeza automática do controle de alterações não remova nenhuma linha que possa ser exigida pela transação aberta no banco de dados que tenha o isolamento de instantâneo habilitado. Mantenha o isolamento de instantâneo confirmado de leitura e as transações de isolamento de sys.syscommittab instantâneo o mais curto possível para garantir que as linhas expiradas sejam limpas em tempo hábil.

Alternativas ao isolamento de instantâneo

Existem alternativas para o uso do isolamento de instantâneo, mas elas exigem mais trabalho para certificar-se de que todos os requisitos do aplicativos sejam atendidos. Para garantir que o last_synchronization_version seja válido e que os dados não sejam removidos pelo processo de limpeza antes que as alterações sejam obtidas, siga estas etapas:

  1. Verificar last_synchronization_version depois das chamadas para CHANGETABLE().

  2. Verifique last_synchronization_version como parte de cada consulta para obter alterações usando CHANGETABLE().

As alterações podem ocorrer depois da versão de sincronização para a próxima enumeração for obtida. Existem duas maneiras para controlar essa situação: A opção que é usada depende do aplicativo e como ele pode controlar os efeitos colaterais de cada abordagem:

  • Ignore as alterações cuja versão é maior que a nova versão de sincronização.

    O efeito colateral dessa abordagem é fazer com que uma nova linha ou uma linha atualizada seja ignorada caso ela tenha sido criada ou atualizada antes da nova versão de sincronização, exceto a atualização posterior. Se houver uma nova linha, um problema de integridade referencial poderá ocorrer se houver uma linha em outra tabela que foi criada que referenciou a linha ignorada. Se houver uma linha existente atualizada, a linha será ignorada e não sincronizada até a próxima vez.

  • Inclua todas as alterações, até mesmo aquelas que contêm uma versão maior que a nova versão de sincronização.

    As linhas com uma versão maior que a nova versão de sincronização serão obtidas na próxima sincronização. Isso deve ser esperado e controlado pelo aplicativo.

Além das duas opções anteriores, você pode criar uma abordagem que combine ambas as opções, dependendo da operação. Por exemplo, talvez você queira um aplicativo para o qual é melhor ignorar as alterações mais recentes do que a próxima versão de sincronização na qual a linha foi criada ou excluída, mas as atualizações não são ignoradas.

Observação

Ao optar pela abordagem que funcionará com o aplicativo quando você estiver usando o controle de alterações (ou qualquer mecanismo de controle personalizado), será preciso considerar uma análise significativa. Portanto, é muito mais simples usar o isolamento de instantâneo.

Como o controle de alterações lida com alterações em um banco de dados

Alguns aplicativos que usam controle de alterações executam sincronização de duas vias com outro repositório de dados. Isto é, as alterações que são feitas no banco de dados do SQL Server são atualizadas no outro armazenamento de dados, e as alterações feitas no outro repositório são atualizadas no banco de dados do SQL Server .

Quando um aplicativo atualiza o banco de dados local com alteração de outro armazenamento de dados, o aplicativo deve executar as seguintes operações:

  • Verifique os conflitos.

    Um conflito acontece quando os mesmos dados são alterados ao mesmo tempo em ambos os armazenamentos de dados. O aplicativo deve poder verificar um conflito e obter informações suficientes para permitir a solução do conflito.

  • Armazene informações de contexto de aplicativo.

    O aplicativo armazena dados que têm as informações de controle de alterações. Essas informações estariam disponíveis junto com outras informações de controle de alterações quando foram obtidas alterações do banco de dados local. Um exemplo comum dessas informações de contexto é um identificador para o repositório de dados que era a origem da alteração.

Para executar as operações anteriores, um aplicativo de sincronização pode usar as funções a seguir:

  • CHANGETABLE(VERSION...)

    Quando um aplicativo estiver fazendo alterações, poderá usar esta função para verificar conflitos. A função obtém as últimas informações de controle de alterações por uma linha especificada em uma tabela de alteração rastreada. As informações do controle de alterações incluem a versão da última vez que a linha foi alterada. Essa informação permite que um aplicativo determine se a linha foi alterada, depois da última vez que o aplicativo foi sincronizado.

  • WITH CHANGE_TRACKING_CONTEXT

    Um aplicativo pode usar esta cláusula para armazenar dados de contexto.

Verificar conflitos

Em um cenário de sincronização bidirecional, o aplicativo cliente deve determinar se uma linha não foi atualizada desde a última vez que o aplicativo obteve as alterações.

O exemplo a seguir mostra como usar a função CHANGETABLE(VERSION ...) para verificar conflitos do modo mais eficiente, sem uma consulta separada. No exemplo, CHANGETABLE(VERSION ...) determina o SYS_CHANGE_VERSION para a linha especificada por @product id. CHANGETABLE(CHANGES ...) pode obter as mesmas informações, mas isso seria menos eficiente. Se o valor da SYS_CHANGE_VERSION linha for maior que o valor de @last_sync_version, haverá um conflito. Se houver um conflito, a linha não será atualizada. A verificação ISNULL() é necessária porque não poderia haver nenhuma informações de alteração disponível para a linha. Nenhuma informação de alteração existiria se a linha não tivesse sido atualizada desde que o controle de alterações foi habilitado ou desde que as informações de alteração foram limpas.

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

O código a seguir pode verificar a conta de linha atualizada e identificar mais informações sobre o conflito.

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

Definir informações de contexto

Usando a cláusula WITH CHANGE_TRACKING_CONTEXT, um aplicativo pode armazenar informações de contexto junto com as informações de alteração. Essas informações podem ser obtidas na coluna SYS_CHANGE_CONTEXT que é retornada por CHANGETABLE(CHANGES ...).

Informações de contexto são normalmente usadas para identificar a origem das alterações. Se a origem da alteração puder ser identificada, aquela informação poderá ser usada por um repositório de dados para evitar a obtenção de alterações quando ele for novamente sincronizado.

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

Garantir resultados consistentes e corretos

Um aplicativo deve considerar o processo de limpeza ao validar o valor de @last_sync_version. Isso é porque dados podem ter sido removidos depois que CHANGE_TRACKING_MIN_VALID_VERSION () foi chamado, mas antes que a atualização fosse feita.

Você deve usar o isolamento de instantâneo e fazer as alterações dentro de uma transação de instantâneo.

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

Observação

Há uma possibilidade de que a linha, que está sendo atualizada dentro da transação de instantâneo, possa ter sido atualizada em uma outra transação depois que a transação de instantâneo foi iniciada. Nesse caso, um conflito de atualização de isolamento do instantâneo acontecerá e a transação será encerrada. Se isso acontecer, tente novamente a atualização. Isso levará então a detecção de um conflito do controle de alterações e nenhuma linha será alterada.

Controle de alterações e restauração de dados

Os aplicativos que requerem sincronização devem considerar o caso de um banco de dados, com o controle de alterações habilitado, reverter a uma versão anterior dos dados. Isso pode ocorrer depois que um banco de dados é restaurado de um backup, quando há um failover em um espelho de banco de dados assíncrono ou quando há uma falha ao usar o envio de logs. O cenário a seguir ilustra o assunto:

  1. A tabela T1 é controlada por alterações e a versão mínima válida para a tabela é 50.

  2. Um aplicativo cliente sincroniza dados com a versão 100 e obtém informações sobre todas as alterações entre as versões 50 e 100.

  3. São feitas alterações adicionais na tabela T1 depois da versão 100.

  4. Na versão 120, há uma falha e o administrador do banco de dados restaura o banco de dados com perda de dados. Depois da operação de restauração, a tabela contém dados até a versão 70, e a versão sincronizada mínima ainda é 50.

    Isso significa que o repositório de dados sincronizado tem dados que já não existem mais no repositório de dados primário.

  5. A T1 é atualizada muitas vezes. Isso traz a versão atual para 130.

  6. O aplicativo cliente é sincronizado novamente e fornece uma última versão sincronizada de 100. O cliente valida esse número com êxito porque 100 é maior do que 50.

    O cliente obtém alterações entre as versões 100 e 130. Neste ponto, o cliente não está ciente de que as alterações entre 70 e 100 não são as mesmas de antes. Os dados no cliente e no servidor não são sincronizados.

Se o banco de dados fosse recuperado para um ponto após a versão 100, não haveria problemas com a sincronização. O cliente e o servidor sincronizarão os dados corretamente durante o próximo intervalo de sincronização.

O controle de alterações não oferece suporte para recuperação da perda de dados. No entanto, há duas opções para detectar esses problemas de sincronização:

  • Armazene um ID da versão do banco de dados no servidor e atualize esse valor sempre que o banco de dados for recuperado ou perder dados. Cada aplicativo cliente armazenará o ID, e cada cliente terá de validar esse ID quando sincronizar os dados. Se ocorrer perda de dados, as IDs não corresponderão e os clientes serão reinicializados. Uma desvantagem é que, se a perda de dados não tiver cruzado o último limite sincronizado, o cliente poderá fazer uma reinicialização desnecessária.

  • Quando um cliente consultar alterações, registre o último número de versão de sincronização para cada cliente no servidor. Se houver um problema com os dados, os últimos números de versão sincronizados não corresponderão. Isso indica que é necessária uma reinicialização.

Confira também