Utiliser le change tracking pour la synchronisation des données

Le change tracking (suivi des modifications) est une des fonctionnalités apparues avec SQL Server 2008. Cette fonctionnalité est très pratique lorsqu'il s'agit d'identifier les données qui ont été ajoutées, modifiées ou supprimées, pour en faire périodiquement la mise à jour dans une autre table dont les données doivent rester synchronisées. L'enregistrement ajouté, mis à jour ou supprimé dans l'intervalle sera facile à localiser à travers des vues système : auparavant, nous aurions écrit un trigger et une table de journalisation pour faire ce travail...!

Une fonctionnalité à ne pas confondre avec le CDC (Change Data Capture ou capture des données modifiées), qui capture toutes les modifications effectuées grâce à une tâche de log reader (version entreprise uniquement).

Un exemple de change tracking pour synchroniser en mode planifié les données entre deux tables.

-- Création d'une base et d'une table source
USE master
GO
CREATE DATABASE ChangeTrackingSource
go
USE ChangeTrackingSource
SELECT  [BusinessEntityID]
      , [PersonType]
      , [FirstName]
      , [LastName]
INTO    person
FROM    AdventureWorks2008R2.Person.Person 
go
ALTER TABLE person
ADD CONSTRAINT pk_person PRIMARY KEY CLUSTERED ( BusinessEntityID )
  -- Une PK est nécessaire pour le change tracking
go
-- Création d'une base et table destination
USE master
go
CREATE DATABASE ChangeTrackingTarget
go
USE ChangeTrackingTarget
Go
CREATE TABLE [SynchroPerson]
    (
      [BusinessEntityID] [int] NOT NULL
    , [PersonType] [nchar](2) NULL
    , [FirstName] NVARCHAR(50) NULL
    , [LastName] NVARCHAR(50) NULL
    , CONSTRAINT [PK_Person_BusinessEntityID] PRIMARY KEY CLUSTERED ( [BusinessEntityID] ASC )
    )
GO
INSERT  SynchroPerson
        SELECT  *
        FROM    ChangeTrackingSource..person
 -- mêmes données
GO
-- Je crée aussi une table pour conserver les versions de la source pour le change tracking
CREATE TABLE CT_SourceVersion ( CT_version BIGINT )
INSERT  CT_SourceVersion
        DEFAULT VALUES
GO

-- Activation du change tracking pour la base et pour la table source
USE master
GO
ALTER DATABASE ChangeTrackingSource SET CHANGE_TRACKING= ON
( CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON
)
GO
USE ChangeTrackingSource
GO
ALTER TABLE person ENABLE CHANGE_TRACKING
    WITH (
         TRACK_COLUMNS_UPDATED = ON)
GO


-- initialisation du change tracking, mes tables sont synchrones et je stocke la version actuelle
UPDATE  ChangeTrackingTarget..CT_SourceVersion
SET     CT_Version = ( SELECT   CHANGE_TRACKING_CURRENT_VERSION()
                     )



-- faire quelques mises à jour, ajouts et suppressions successifs dans ma source
UPDATE  Person
SET     FirstName = 'paul'
      , LastName = 'allen'
WHERE   BusinessEntityID = 1
go
UPDATE  Person
SET     FirstName = 'bill'
      , LastName = 'gates'
WHERE   BusinessEntityID = 1
go
INSERT  Person
        (
          BusinessEntityID
        , PersonType
        , FirstName
        , LastName
        )
VALUES  (
          310
        , 'SC'
        , 'Joan'
        , 'Ballmer'
        )
go
UPDATE  Person
SET     FirstName = 'steve'
WHERE   BusinessEntityID = 310
GO
DELETE  person
WHERE   BusinessEntityID = 2
GO

----------- Le change tracking, je vais synchroniser mes tables ----
SELECT  *
FROM    ChangeTrackingTarget..SynchroPerson
WHERE   BusinessEntityID IN ( 1, 2, 310 )
   -- juste pour montrer que les modification ne sont pas dans ma cible

DECLARE @sync_version BIGINT
SELECT  @sync_version = CT_version
FROM    ChangeTrackingTarget..CT_SourceVersion
   -- je récupère la version
-- et j'affiche les modifications de ma source depuis cette version
SELECT  CT.BusinessEntityID
      , p.persontype
      , p.firstname
      , p.lastname
      , CT.SYS_CHANGE_OPERATION
      , CT.SYS_CHANGE_COLUMNS
      , CT.SYS_CHANGE_CONTEXT
FROM    CHANGETABLE(CHANGES Person, @sync_version) CT
        LEFT JOIN Person p ON CT.BusinessEntityID = p.BusinessEntityID


-- Je peux donc synchroniser : INSERT, UPDATE, DELETE
DECLARE @sync_version BIGINT
SELECT  @sync_version = CT_version
FROM    ChangeTrackingTarget..CT_SourceVersion

INSERT  ChangeTrackingTarget..SynchroPerson
        (
          BusinessEntityID
        , PersonType
        , FirstName
        , LastName
        )
        SELECT  CT.BusinessEntityID
              , p.persontype
              , p.firstname
              , p.lastname
        FROM    CHANGETABLE(CHANGES Person, @sync_version) CT
                JOIN Person p ON CT.BusinessEntityID = p.BusinessEntityID
                                 AND CT.SYS_CHANGE_OPERATION = 'I'

UPDATE  ChangeTrackingTarget..SynchroPerson
SET     FirstName = p.Firstname
      , LastName = p.Lastname
      , PersonType = p.Persontype
FROM    ChangeTrackingTarget..SynchroPerson
        JOIN CHANGETABLE(CHANGES Person, @sync_version) CT ON ChangeTrackingTarget..SynchroPerson.BusinessEntityID = CT.BusinessEntityID
                                                              AND CT.SYS_CHANGE_OPERATION = 'U'
        JOIN Person p ON CT.BusinessEntityID = p.BusinessEntityID

DELETE  ChangeTrackingTarget..SynchroPerson
WHERE   BusinessEntityID in (
        SELECT  CT.BusinessEntityID
        FROM    CHANGETABLE(CHANGES Person, @sync_version) CT
        WHERE   CT.SYS_CHANGE_OPERATION = 'D' )

-- Vérifions
SELECT  *
FROM    ChangeTrackingTarget..SynchroPerson
WHERE   BusinessEntityID IN ( 1, 2, 310 )

-- si tout s'est bien passé, je conserve le nouveau numéro de version pour la prochaine fois
UPDATE  ChangeTrackingTarget..CT_SourceVersion
SET     CT_Version = ( SELECT   CHANGE_TRACKING_CURRENT_VERSION()
                     )