Compartir a través de


Obtener los cambios con las funciones de seguimiento de cambios

En este tema se describen las funciones de seguimiento de cambios de SQL Server 2008 y la forma de usarlas para obtener los cambios que se realizan en una base de datos, así como información sobre ellos.

Acerca de las funciones de seguimiento de cambios

Las aplicaciones pueden utilizar las funciones siguientes para obtener los cambios que se realizan en una base de datos e información sobre ellos:

  • Función CHANGETABLE(CHANGES…)
    Esta función de conjunto de filas se utiliza para consultar la información sobre los cambios. La función consulta los datos almacenados en las tablas internas de seguimiento de cambios. La función devuelve un conjunto de resultados que contiene las claves principales de las filas que han cambiado, junto con información adicional sobre los cambios, como la operación, las columnas actualizadas y la versión de la fila.

    CHANGETABLE(CHANGES …) toma como argumento el número de la última versión de sincronización. La última versión de sincronización se obtiene utilizando la variable @last_synchronization_version, como se muestra en los ejemplos de este tema. La semántica de la última versión de sincronización es la siguiente:

    • El cliente que realiza la llamada ha obtenido los cambios y sabe todos los que se han realizado hasta la última versión de sincronización, inclusive.

    • CHANGETABLE(CHANGES …) devolverá por lo tanto todos los cambios que se han producido después de la última versión de sincronización.

      La ilustración siguiente muestra cómo se usa CHANGETABLE(CHANGES...) para obtener los cambios.

      Ejemplo de resultado de una consulta de seguimiento de cambios

  • Función CHANGE_TRACKING_CURRENT_VERSION()
    Se utiliza para obtener la versión actual que se utilizará la próxima vez que se consulten los cambios. Esta versión representa la versión de la última transacción confirmada.

  • Función CHANGE_TRACKING_MIN_VALID_VERSION()
    Se utiliza para obtener el número de versión mínimo válido que un cliente puede tener y seguir obteniendo resultados válidos de CHANGETABLE(). El cliente debe comparar la última versión de sincronización con el valor devuelto por esta función. Si el último número de versión de sincronización es menor que el de la versión devuelta por esta función, el cliente no podrá obtener resultados válidos de CHANGETABLE() y tendrá que reinicializar.

Obtener los datos iniciales

Para que una aplicación pueda obtener los cambios por primera vez, debe enviar una consulta que obtenga los datos iniciales y la versión de sincronización. La aplicación debe obtener directamente los datos adecuados en la tabla y, a continuación, usar CHANGE_TRACKING_CURRENT_VERSION() para obtener la versión inicial. Esta versión se pasará a CHANGETABLE(CHANGES…) la primera vez que se obtengan los cambios.

En el ejemplo siguiente se muestra cómo se obtienen los datos de la versión de sincronización inicial y el conjunto de datos inicial.

    -- 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 las funciones de seguimiento de cambios para obtener los cambios

Para obtener las filas cambiadas de una tabla e información sobre los cambios, utilice CHANGETABLE(CHANGES...). Por ejemplo, la consulta siguiente obtiene los cambios de la tabla 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

Normalmente, un cliente deseará obtener los últimos datos de una fila y no solo las claves principales de la fila. Por consiguiente, una aplicación combinaría los resultados de CHANGETABLE(CHANGES...) con los datos de la tabla de usuario. Por ejemplo, la siguiente consulta se combina con la tabla SalesLT.Product para obtener los valores de las columnas Name y ListPrice. Observe el uso de la propiedad OUTER JOIN. Esto es necesario para asegurarse de que la información sobre los cambios se devuelve para las filas que han sido eliminadas de la tabla de usuario.

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 obtener la versión que se usará en la enumeración de cambios siguiente, utilice CHANGE_TRACKING_CURRENT_VERSION(), como se muestra en el ejemplo siguiente.

SET @synchronization_version = CHANGE_TRACKING_CURRENT_VERSION()

Cuando una aplicación obtiene los cambios, debe utilizar CHANGETABLE(CHANGES...) y CHANGE_TRACKING_CURRENT_VERSION(), como se muestra en el ejemplo siguiente.

-- 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 versión

Una base de datos con el seguimiento de cambios habilitado tiene un contador de versión que se incrementa cuando se realizan cambios en las tablas sometidas a seguimiento. Cada fila modificada tiene un número de versión asociado a ella. Cuando una solicitud se envía a una aplicación para consultar los cambios, se llama a una función que proporciona un número de versión. La función devuelve información sobre todos los cambios realizados desde esa versión. En cierto modo, la versión del seguimiento de cambios es similar en su concepto al tipo de datos rowversion.

Validar la última versión sincronizada

La información sobre los cambios se mantiene durante un tiempo limitado. El parámetro CHANGE_RETENTION, que se puede especificar como parte de la instrucción ALTER DATABASE, controla el intervalo de tiempo.

Tenga en cuenta que el tiempo especificado para CHANGE_RETENTION determina la frecuencia con que todas las aplicaciones deben solicitar los cambios de la base de datos. Si una aplicación tiene un valor para last_synchronization_version que es anterior a la versión de sincronización válida mínima para una tabla, esa aplicación no puede realizar una enumeración válida de los cambios. Esto se debe a que podría haberse limpiado parte de la información de los cambios. Antes de que una aplicación obtenga los cambios mediante CHANGETABLE(CHANGES...), debe validar el valor de last_synchronization_version que planea pasar a CHANGETABLE(CHANGES...). Si el valor de last_synchronization_version no es válido, esa aplicación debe reinicializar todos los datos.

En el ejemplo siguiente se muestra cómo comprobar la validez del valor de last_synchronization_version para cada tabla.

    -- 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 se muestra en el ejemplo siguiente, la validez del valor de last_synchronization_version se puede comprobar con todas las tablas de la base de datos.

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

Usar el seguimiento de columnas

El seguimiento de columnas permite a las aplicaciones obtener solo los datos de las columnas que han cambiado en lugar de la fila entera. Por ejemplo, considere el escenario en el que una tabla tiene una o varias columnas que son grandes, pero que raramente cambian. Además, hay otras columnas que cambian con frecuencia. Sin el seguimiento de columnas, una aplicación solo puede determinar que una fila ha cambiado y tendría que sincronizar todos los datos, incluidos los de las columnas grandes. Sin embargo, mediante el seguimiento de columnas, una aplicación puede determinar si los datos de las columnas grandes cambiaron y solo puede sincronizar los cambios en caso afirmativo.

La información de seguimiento de columnas aparece en la columna SYS_CHANGE_COLUMNS devuelta por la función CHANGETABLE(CHANGES…).

Se puede usar el seguimiento de columnas para que se devuelva NULL cuando una columna no haya cambiado. Si la columna se puede cambiar a NULL, se debe devolver una columna independiente para indicar si la columna cambió.

En el ejemplo siguiente, la columna CT_ThumbnailPhoto será NULL si esa columna no cambió. Esta columna también podría ser NULL si cambió a NULL; la aplicación puede usar la columna CT_ThumbNailPhoto_Changed para determinar si la columna cambió.

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'

Obtener resultados coherentes y correctos

Para obtener los datos que han cambiado de una tabla, se requieren varios pasos. Tenga en cuenta que se pueden devolver resultados incoherentes o incorrectos si no se consideran y se tratan ciertos problemas.

Por ejemplo, para obtener los cambios que se realizaron en las tablas Sales y SalesOrders, una aplicación realizaría los pasos siguientes:

  1. Validar la última versión sincronizada con CHANGE_TRACKING_MIN_VALID_VERSION().

  2. Obtener la versión que puede utilizarse la próxima vez para obtener los cambios mediante CHANGE_TRACKING_CURRENT_VERSION().

  3. Obtener los cambios de la tabla Sales con CHANGETABLE(CHANGES …).

  4. Obtener los cambios de la tabla SalesOrders con CHANGETABLE(CHANGES …).

Se están produciendo dos procesos en la base de datos que pueden afectar a los resultados que devuelven los pasos anteriores:

  • El proceso de limpieza se ejecuta en segundo plano y quita la información de seguimiento de cambios cuya antigüedad supere el período de retención especificado.

    El proceso de limpieza es un proceso en segundo plano independiente que usa el período de retención que se especifica al configurar el seguimiento de cambios para la base de datos. El problema es que el proceso de limpieza se puede producir en el período que transcurre entre el momento en que se validó la última versión de sincronización y el momento en que se realiza la llamada a CHANGETABLE(CHANGES...). Una última versión de sincronización que era válida en ese momento podría no serlo cuando se obtienen los cambios. Por consiguiente, se podrían devolver resultados incorrectos.

  • Se están produciendo operaciones DML continuas en las tablas Sales y SalesOrders, como las siguientes:

    • Se pueden realizar cambios en las tablas después de obtenerse la siguiente versión utilizando CHANGE_TRACKING_CURRENT_VERSION(). Por lo tanto, se pueden devolver más cambios que los esperados.

    • Una transacción podría confirmarse en el período de tiempo que transcurre entre la llamada para obtener los cambios de la tabla Sales y la llamada para obtener los cambios de la tabla SalesOrders. Por consiguiente, los resultados de la tabla SalesOrder podrían tener un valor de clave externa que no existe en la tabla Sales.

Para superar los desafíos enumerados anteriormente, se recomienda utilizar el aislamiento de instantánea. Esto ayudará a garantizar la coherencia de la información de los cambios y a evitar las condiciones de anticipación relacionadas con la tarea de limpieza en segundo plano. Si no utiliza transacciones de instantáneas, el desarrollo de una aplicación que use el seguimiento de cambios puede requerir mucho más esfuerzo.

Usar el aislamiento de instantánea

El seguimiento de cambios se ha diseñado para que funcione bien con el aislamiento de instantánea. El aislamiento de instantánea se debe habilitar para la base de datos. Todos los pasos necesarios para obtener los cambios deben estar incluidos dentro de una transacción de instantánea. De este modo se garantizará que todos los cambios que se realicen en los datos mientras se obtienen los cambios no serán visibles para las consultas dentro de la transacción de instantánea.

Para obtener los datos dentro de una transacción de instantánea, realice los pasos siguientes:

  1. Establezca el nivel de aislamiento de transacción en la instantánea e inicie una transacción.

  2. Valide la última versión de sincronización con CHANGE_TRACKING_MIN_VALID_VERSION().

  3. Obtenga la siguiente versión que se va a usar utilizando CHANGE_TRACKING_CURRENT_VERSION().

  4. Obtenga los cambios de la tabla Sales con CHANGETABLE(CHANGES …)

  5. Obtenga los cambios de la tabla Salesorders con CHANGETABLE(CHANGES …)

  6. Confirme la transacción.

Algunas cuestiones que hay que recordar ya que todos los pasos para obtener los cambios están dentro de una transacción de instantáneas son:

  • Si la limpieza se produce una vez validada la última versión de sincronización, los resultados de CHANGETABLE(CHANGES...) seguirán siendo válidos ya que las operaciones de eliminación realizadas en el proceso de limpieza no estarán visibles dentro de la transacción.

  • Cualquier cambio que se efectúe en las tablas Sales o SalesOrders una vez obtenida la versión de sincronización siguiente no será visible y las llamadas a CHANGETABLE(CHANGES…) nunca devolverán cambios con una versión posterior a la devuelta por CHANGE_TRACKING_CURRENT_VERSION(). La coherencia entre las tablas Sales y SalesOrders también se mantendrá porque las transacciones que se confirmaron entre las llamadas a CHANGETABLE(CHANGES...) no serán visibles.

En el ejemplo siguiente se muestra cómo se habilita el aislamiento de instantánea para una base de datos.

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

Una transacción de instantánea se utiliza como sigue:

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 obtener más información acerca de las transacciones de instantánea, vea Usar niveles de aislamiento basado en versiones de filas.

Alternativas al uso del aislamiento de instantánea

Hay alternativas al uso del aislamiento de instantánea, pero exigen más trabajo para garantizar que se cumplan todos los requisitos de la aplicación. Para garantizar que el valor de last_synchronization_version sea válido y que el proceso de limpieza no quite los datos antes de que se obtengan los cambios, haga lo siguiente:

  1. Compruebe el valor de last_synchronization_version después de las llamadas a CHANGETABLE().

  2. Compruebe el valor de last_synchronization_version como parte de cada consulta para obtener los cambios con CHANGETABLE().

Los cambios se pueden producir después de haberse obtenido la versión de sincronización de la enumeración siguiente. Hay dos formas de controlar esta situación. La opción utilizada depende de la aplicación y de cómo puede administrar los efectos secundarios de cada enfoque:

  • Omita los cambios que tengan una versión mayor que la nueva versión de sincronización.

    Este enfoque tiene el efecto secundario de que una fila nueva o actualizada se omitiría si se creó o se actualizó antes de la nueva versión de sincronización, aunque se actualizase posteriormente. Si hay una fila nueva, se podría producir un problema de integridad referencial si hubiera una fila de otra tabla creada que hiciera referencia a la fila omitida. Si hay una fila existente actualizada, se omitirá y no se sincronizará hasta la próxima vez.

  • Incluya todos los cambios, incluso los que tengan una versión mayor que la nueva versión de sincronización.

    Las filas que tengan una versión mayor que la nueva versión de sincronización volverán a obtenerse en la sincronización siguiente. La aplicación debe prever y controlar este punto.

Además de las dos opciones anteriores, puede idear un enfoque que combine ambas opciones, dependiendo de la operación. Por ejemplo, puede querer crear una aplicación para la que sea mejor omitir los cambios más recientes que la versión de sincronización siguiente en la que se creó o se eliminó la fila, pero en la que no se omitan las actualizaciones.

Nota

La elección del enfoque que va a funcionar para la aplicación cuando se usa el seguimiento de cambios (o cualquier mecanismo de seguimiento personalizado), requiere un análisis significativo. Por consiguiente, es mucho más fácil utilizar el aislamiento de instantánea.

Historial de cambios

Contenido actualizado

Se ha corregido un error tipográfico en el ejemplo de código para comprobar la validez de last_synchronization_version con todas las tablas de la base de datos.