如何为协作同步设置服务器数据库(非 SQL Server)

备注

本节文档中的主题同步其他与 ADO.NET 兼容的数据库旨在说明如何通过使用 Sync Framework 同步并非 SQL Server 的数据库。在此版本中,SQL Server 用于代码示例中,但通过对特定于 SQL Server 的对象(例如 SqlConnection)以及所示的 SQL 查询进行一些修改,这些代码可用于其他与 ADO.NET 兼容的数据库。有关 SQL Server 同步的信息,请参见如何配置和执行协作同步 (SQL Server)

本主题介绍如何设置 DbSyncProvider 同步的数据库,以便可以在该数据库中跟踪增量数据变更。对变更进行跟踪,以便它们在同步会话期间可以应用到其他节点。要为 Sync Framework 设置数据库,请按照以下步骤执行:

  1. 为数据库启用快照隔离

  2. 标识要同步的表

  3. 创建跟踪表来存储每个表的元数据;并且对这些表创建索引

  4. 对每个基表创建触发器,以便填充和更新跟踪表

  5. (可选)处理数据库中的现有数据

  6. 创建一个跟踪表来存储每个作用域的元数据;并且在该表上创建一个索引

  7. 定义要同步的作用域,指定哪些表作为一个单元同步

  8. 创建存储过程以便选择和更新数据和元数据

这些步骤不是 SqlCeSyncProvider 同步的数据库所必需的;在初始化数据库时由 Sync Framework 处理设置。

设置某一数据库后,该数据库可与其他节点同步。有关如何配置和执行同步的更多信息,请参见如何配置和执行协作同步(非 SQL Server)

为数据库启用快照隔离

在同步会话的变更枚举阶段中,Sync Framework 在快照隔离下启动事务。要在快照隔离下启动事务,您必须将 ALLOW_SNAPSHOT_ISOLATION 数据库选项设置为 ON,如以下代码示例所示:

ALTER DATABASE [database name] SET ALLOW_SNAPSHOT_ISOLATION ON

有关更多信息,请参见 SQL Server 联机丛书。

标识要同步的表

设置数据库的第一步是标识将同步的表。每个表都必须有一个主键。请看下面的代码示例。它显示 SyncSamplesDb_Peer1 数据库中的 Sales.Customer 表架构。

CREATE TABLE Sales.Customer(
    CustomerId uniqueidentifier NOT NULL PRIMARY KEY DEFAULT NEWID(), 
    CustomerName nvarchar(100) NOT NULL,
    SalesPerson nvarchar(100) NOT NULL,
    CustomerType nvarchar(100) NOT NULL)

您同步的每个表都具有关联的 DbSyncAdapter 对象,并且您在 RowIdColumns 集合中为该对象指定主键。有关更多信息,请参见如何配置和执行协作同步(非 SQL Server)

表可以为空,也可以包含现有数据。如果该表包含应同步的现有数据行,则您必须确保每一行在相应的变更跟踪表中都具有元数据条目。有关更多信息,请参见处理数据库中的现有数据。

创建用于每个表元数据的跟踪表

Sync Framework 需要一个方法来跟踪两个节点间自上次同步会话后哪些行已变更。变更由两种不同类型的元数据表示:

  • 每个表的元数据,用于跟踪同步的每个表的插入、更新和删除操作。

  • 每个作用域的元数据,用于跟踪每个节点从其他节点接收到的变更。

通过为每个基表使用一个跟踪表来跟踪每个表的元数据。基表和跟踪表必须在 DbSyncProvider 同步的每个数据库中存在。跟踪表的主键与基表中的主键相同,但还需要使用一些附加列。下表对这些列进行了说明。附加列的名称不必与表中所列的名称相同;但它们必须匹配访问跟踪表的查询或过程的顺序和类型。创建用于选择和更新数据和元数据的存储过程中包含了其中一些过程。

说明 何时更新…

<基表的主键 (PK)> - 为每一 PK 列包括一列。

基表的主键列。

将一行插入到基表中。此插入可以源自本地节点或远程节点。

update_scope_local_id

执行了最后更新或删除的作用域的 ID。对于源于本地节点的更新或删除,该列为 NULL。

请参考作用域信息表中的 scope_local_id 列。有关更多信息,请参见“创建每个作用域的元数据的跟踪表”。

来自远程节点的更新或删除将应用于基表。

scope_update_peer_key

执行了最后更新或删除的节点的标识。

来自远程节点的更新或删除将应用于基表。

scope_update_peer_timestamp

最初更新或删除行时在远程数据库的时间戳值。

来自远程节点的更新或删除将应用于基表。

local_update_peer_key

本地节点的标识。此列将为每一行包含值 0,除非本地数据库是从备份还原的。1

来自本地操作或远程节点的更新或删除将应用于基表。

local_update_peer_timestamp

在本地数据库中更新或删除行时在本地数据库的时间戳值。1

来自本地操作或远程节点的更新或删除将应用于基表。

create_scope_local_id

执行了插入的作用域的标识。对于源于本地节点的更新或删除,该列为 NULL。

请参考作用域信息表中的 scope_local_id 列。有关更多信息,请参见如何为协作同步设置服务器数据库(非 SQL Server)中的“创建每个作用域的元数据的跟踪表”。

来自远程节点的插入将应用于基表。

scope_create_peer_key

执行了插入的节点的标识。

来自远程节点的插入将应用于基表。

scope_create_peer_timestamp

在最初插入行时在远程数据库的时间戳值。

来自远程节点的插入将应用于基表。

local_create_peer_key

本地节点的标识。此列将为每一行包含值 0,除非本地数据库是从备份还原的。1

来自本地操作或远程节点的插入将应用于基表。

local_create_peer_timestamp

在行插入到本地数据库中时在本地数据库的时间戳值。1

来自本地操作或远程节点的插入将应用于基表。

sync_row_is_tombstone

值为 1 表示元数据条目用于基表中的删除操作。

从基表中删除一行。此删除可以源自本地节点或远程节点。

last_change_datetime

上一次更新元数据行的日期和时间。

插入或更新此跟踪表中的一行。

restore_timestamp

在数据库还原时存储 local_update_peer_timestamp 的值。然后将该值用作本地更新时间戳值。

该值通常为 NULL,但可由还原过程设置。只要更新行就设置为 NULL。

<筛选列> - 为在任何作用域的筛选 WHERE 子句中使用的每个非 PK 列添加一列。

只有在为一个或多个作用域筛选表时才是必需的。存储筛选列的针对插入、更新和删除操作的值。

在基表中插入、更新或删除一行。此删除可以源自本地节点或远程节点。

1 在同步重叠的作用域时由 Sync Framework 使用。请考虑以下针对某一数据库的更新示例,该数据库将作用域 X 与客户端 A 同步,将作用域 Y 与客户端 B 同步。这两个作用域都包含行 Q。

  1. 行 Q 在客户端 A 被更新,然后与数据库同步。

  2. 客户端 B 与数据库同步,并接收对行 Q 的更新。

    客户端 B 不知道作用域 X,因此,来自客户端 A 的变更必须表现为就像源自数据库。这是在与客户端 B 或者任何其他客户端(该客户端不同步在行 Q 的 update_scope_local_id 中存储的作用域)同步时,通过使用来自 local_update_peer_key 和 local_update_peer_timestamp 的值实现的。

  3. 行 Q 在数据库被更新,然后与客户端 A 同步。

    客户端 A 知道作用域 X,这样,在与客户端 A 或者同步作用域 X 的任何其他客户端同步时使用来自 scope_update_peer_key 和 scope_update_peer_timestamp 的值。

我们建议您为跟踪表和与同步元数据相关的所有其他对象分别创建单独的数据库架构。这有助于从基表的数据中隔离元数据。为获得最佳的性能,在每个跟踪表上都创建索引:

  • 如果未筛选数据:使用与基表相同的主键;并且在 (local_update_peer_timestamp) 上创建一个非聚集索引

  • 如果筛选数据:使用与基表相同的主键;并且在 (local_update_peer_timestamp, <filter columns>, <primary key columns>) 上创建一个非聚集索引

下面的代码示例在 Sync 架构中创建了一个表,用于跟踪 Sales.Customer 表的变更;并且向该表添加索引。

CREATE TABLE Sync.Customer_Tracking(
    
    CustomerId uniqueidentifier NOT NULL PRIMARY KEY,          
    
    update_scope_local_id int NULL, 
    scope_update_peer_key int,
    scope_update_peer_timestamp bigint,
    local_update_peer_key int,
    local_update_peer_timestamp timestamp,

    create_scope_local_id int NULL,
    scope_create_peer_key int,
    scope_create_peer_timestamp bigint,
    local_create_peer_key int,
    local_create_peer_timestamp bigint,

    sync_row_is_tombstone int, 
    restore_timestamp bigint, 
    last_change_datetime datetime default NULL)

CREATE NONCLUSTERED INDEX NonClustered_Customer_Tracking
ON Sync.Customer_Tracking ([local_update_peer_timestamp])

创建用于填充和更新跟踪表的触发器

创建跟踪表后,将 INSERT、UPDATE 和 DELETE 触发器添加到每个基表。在用户或 Sync Framework 在基表中插入、更新或删除行时,触发器将引发并且该行的元数据将在变更跟踪表中插入或更新。如果 Sync Framework 已将变更应用于基表(因为它来自其他节点),Sync Framework 将更新变更跟踪表以反映变更的来源。

以下代码示例将创建一个触发器,该触发器将在对 Sales.Customer 表进行更新时更新 Sales.Customer_Tracking 表中的变更跟踪元数据。有关插入和删除触发器的示例,请参见用于数据库提供程序帮助主题的安装脚本

CREATE TRIGGER Customer_UpdateTrigger ON Sales.Customer FOR UPDATE
AS    
    UPDATE t    
    SET 
        update_scope_local_id = NULL, local_update_peer_key = 0, 
        restore_timestamp = NULL, last_change_datetime = GetDate() 
    FROM Sync.Customer_Tracking t JOIN inserted i ON t.[CustomerId] = i.[CustomerId]        

处理数据库中的现有数据

通过对基表的触发器,插入和更新每个表的变更跟踪元数据。因此,变更跟踪表不包含与在添加触发器前已插入基表的任何行有关的信息。若要处理数据库中的现有数据,应为现有的数据插入元数据。然后,在第一个同步会话过程中,所有行都作为新插入发送到目标数据库。以下代码示例显示已将触发器添加到基表后为每个基表执行的命令:

INSERT INTO [tracking table] ([pk columns], create_scope_local_id, local_create_peer_key, local_create_peer_timestamp, update_scope_local_id, local_update_peer_key, restore_timestamp, sync_row_is_tombstone)
SELECT [pk columns], NULL, 0, @@DBTS+1, NULL, 0, NULL, 0 from [base table] baseT left outer join [tracking table] trackingT
On baseT.[pk columns]=trackingT.[pk columns]
where tracking.[pk columns] is null

创建每个作用域的元数据的跟踪表

通常在每个数据库中使用两个表跟踪每个作用域的元数据:

  • 此作用域信息表以二进制格式存储每个作用域的同步知识。作用域是定义哪些数据应作为一个单元进行同步的表的逻辑分组。

  • 作用域映射表标识数据库中属于特定作用域的表。一个表可能会属于多个作用域。该映射表必须为每个 <作用域, 表> 对包含一个条目。

Sync Framework 使用知识确定在同步期间要将哪些变更发送到每个数据库。应用程序不必直接使用知识。请考虑具有三个节点的双向同步拓扑:

  1. Node1 和 Node2 同步所有变更。

  2. Node1 与 Node3 同步。

  3. 一个用户在 Node2 上执行更新。

  4. Node3 与 Node2 同步。

在 Node3 与 Node2 同步时,Node3 已具有来自 Node2 的大多数变更,因为 Node3 首先与 Node1 同步。知识使 Sync Framework 能够认识到这一情况,以便只同步在 Node2 上发生的更新。有关知识的更多信息,请参见了解同步知识

下表对作用域信息表的列进行了说明。

说明 何时更新…

scope_id

作用域的标识符,通常是 GUID。

永远不会更新

scope_local_id

作用域的整数标识符。这应该是一个 IDENTITY 列。

永远不会更新

scope_name

作用域的名称。

永远不会更新

scope_sync_knowledge

每个作用域的同步知识的二进制表示形式。

所有变更都已应用于同步会话的目标。

scope_tombstone_cleanup_knowledge

每个作用域的“遗忘知识”的二进制表示形式。遗忘知识用于已清理的元数据。

所有变更都已应用于同步会话的目标。

scope_timestamp

上一次更新元数据行时的时间戳值。

更新此跟踪表中的一行。

scope_cleanup_timestamp

为此作用域执行最近的逻辑删除清除时的时间戳值。

为具有重叠表的另一个作用域清理逻辑删除。

下面的代码示例创建一个作用域信息表。

CREATE TABLE Sync.ScopeInfo(       
    scope_local_id int IDENTITY(1,1),
    scope_id uniqueidentifier default NEWID(),
    scope_name nvarchar(100) NOT NULL PRIMARY KEY,
    scope_sync_knowledge varbinary(max) NULL,
    scope_tombstone_cleanup_knowledge varbinary(max) NULL,
    scope_timestamp timestamp,
    scope_cleanup_timestamp bigint)

几乎总是要对该作用域信息表进行查询,以便基于作用域名称检索与特定作用域有关的信息。因此,对 scope_name 列定义主键。

下表对作用域映射表的列进行了说明。

说明 何时更新…

table_name

表的名称。

永远不会更新

scope_name

作用域的名称。

永远不会更新

下面的代码示例创建一个作用域映射表以及该表上的索引。

CREATE TABLE Sync.ScopeTableMap(       
    scope_name nvarchar(100) ,
    table_name nvarchar(100)     
    )

CREATE UNIQUE CLUSTERED INDEX Clustered_ScopeTableMap ON Sync.ScopeTableMap(scope_name, table_name)

定义要同步的作用域

在创建作用域表后,决定要同步的一个或多个作用域。例如,您可以定义名为 Sales 的一个作用域并在该作用域中包括表 CustomerCustomerContactOrderHeaderOrderDetail。在同步 Sales 作用域时,在两个节点之间交换对上述四个表的变更。定义作用域的过程分为以下两部分:

  1. 向作用域信息表和作用域映射表添加条目,如以下代码示例所示。

    INSERT INTO Sync.ScopeInfo(scope_name) VALUES (''Sales'')
    INSERT INTO Sync.ScopeTableMap(scope_name, table_name) VALUES (''Sales'', ''Sales.Customer'')
    INSERT INTO Sync.ScopeTableMap(scope_name, table_name) VALUES (''Sales'', ''Sales.CustomerContact'')
    
  2. DbSyncProvider 对象的 ScopeName 属性指定作用域名称,并且为您要在该作用域中包括的每个表添加一个 DbSyncAdapter 对象。有关更多信息,请参见如何配置和执行协作同步(非 SQL Server)中的“ScopeName 和 Connection 的应用程序代码”。

Note重要事项

在首次同步某一作用域后,该作用域不应变更。在作用域中变更表或筛选这些表的子句可能导致数据无法收敛。

经过筛选和重叠的作用域

如果基表中只有一部分的行包括在某一作用域中,则对该作用域进行筛选。例如,您可以定义一个名为 sales-WA 的经过筛选的作用域,该作用域只包含华盛顿州的销售数据。要筛选这些数据,为 DbSyncAdapter 对象的 SelectIncrementalChangesCommand 属性指定的查询或过程必须包括用于选择适当数据的 WHERE 子句。此查询或过程应基于跟踪表中的筛选器列(而非基表中的筛选器列)来选择变更。

不支持下列类型的筛选:

  • 列筛选:所有列必须包括在选择和应用变更的查询或过程中。

  • 对用于筛选的列的更新:如果某一用户更新列中用于筛选的值,则一个行将从一个作用域移到另一个作用域。该行将发送到该行现在所属于的新的作用域,但该行不从旧作用域中删除。

如果在它们之间共享公用数据,则这两个作用域将重叠。例如,表 products 可以包括在 sales 作用域和 inventory 作用域中。作用域可以既重叠又经过了筛选。以下应用场景说明筛选和重叠可能发生的方式:

  • 应用场景 1:

    • 作用域 1 是 sales-WA。该作用域包括:productsorders,具有 state=WA 的筛选器;以及 order_details,具有 state=WA 的筛选器。

    • 作用域 2 是 sales-OR。该作用域包括:productsorders,具有 state=OR 的筛选器;以及 order_details,具有 state=OR 的筛选器。

    在这个应用场景中,整个 products 表由两个作用域共享。orders 表和 order_details 表都位于这两个作用域中,但筛选器不重叠;因此,这两个作用域不共享这些表中的行。

  • 应用场景 2:

    • 作用域 1 是 sales-WA。该作用域包括:productsorders,具有 state=WA 的筛选器;以及 order_details,具有 state=WA 的筛选器。

    • 作用域 2 是 sales-Northwest。该作用域包括:productsorders,具有 state=WA OR state=ID 的筛选器;以及 shippers

    在这个应用场景中,整个 products 表再次由两个作用域共享。orders 表位于这两个作用域中并且筛选器重叠:这两个作用域共享满足筛选器 state=WA 的行。shippers 表和 order_details 表在这两个作用域之间不共享。

有许多不同的方式可用于定义作用域,但它们都必须遵守以下原则:在同步拓扑中的一对数据库之间同步的任何数据只能属于一个作用域。例如,在上面的应用场景 2 中,数据库 A 和数据库 B 可以同步作用域 1;并且数据库 A 和数据库 C 可以同步作用域 2。数据库 A 和数据库 B 不能也同步作用域 2,因为 products 行和 orders 行属于这两个作用域。

创建存储过程以便选择和更新数据和元数据

在您创建元数据表后,创建 SQL 查询或存储过程(推荐)以便选择并将变更应用于基表和元数据表。出于性能和安全性原因,建议使用存储过程。为以下 DbSyncAdapter 命令指定这些查询或过程。在如何配置和执行协作同步(非 SQL Server)的“同步适配器”中描述了这些命令。

下面的代码示例创建一组存储过程,以便为 Sales.Customer 表处理数据和元数据变更。为简洁起见,包括了用于选择数据和处理更新的过程,但未包括用于插入和删除的过程。有关插入和删除过程的示例,请参见用于数据库提供程序帮助主题的安装脚本。模板使您能够更轻松地创建所有这些存储过程,请参见服务器设置模板了解有关模板的信息。

在本主题末尾的完整代码示例中,传递到这些过程的许多值都来自“会话变量”**。这些变量是内置变量,使 Sync Framework 可以在同步会话期间将值传递到命令。有关会话变量的更多信息,请参见如何使用协作同步的会话变量

用于 SelectIncrementalChangesCommand 的过程

create procedure Sync.sp_Customer_SelectChanges (
    @sync_min_timestamp bigint,
    @sync_metadata_only int,
    @sync_scope_local_id int,
    @sync_initialize int
)
as

--if @sync_initialize = 0
--begin
    -- Perform additional logic if required.
--end
    
begin
    select  t.CustomerId, 
            c.CustomerName,
            c.SalesPerson,
            c.CustomerType, 
            t.sync_row_is_tombstone,
            t.local_update_peer_timestamp as sync_row_timestamp, 
            case when (t.update_scope_local_id is null or t.update_scope_local_id <> @sync_scope_local_id) 
                 then case when (t.restore_timestamp is null) then t.local_update_peer_timestamp else t.restore_timestamp end else t.scope_update_peer_timestamp end as sync_update_peer_timestamp,
            case when (t.update_scope_local_id is null or t.update_scope_local_id <> @sync_scope_local_id) 
                 then t.local_update_peer_key else t.scope_update_peer_key end as sync_update_peer_key,
            case when (t.create_scope_local_id is null or t.create_scope_local_id <> @sync_scope_local_id) 
                 then t.local_create_peer_timestamp else t.scope_create_peer_timestamp end as sync_create_peer_timestamp,
            case when (t.create_scope_local_id is null or t.create_scope_local_id <> @sync_scope_local_id) 
                 then t.local_create_peer_key else t.scope_create_peer_key end as sync_create_peer_key
    from Sales.Customer c right join Sync.Customer_Tracking t on c.CustomerId = t.CustomerId
    where t.local_update_peer_timestamp > @sync_min_timestamp
end

用于 UpdateCommand 的过程

CREATE PROCEDURE Sync.sp_Customer_ApplyUpdate (                                    
        @CustomerId uniqueidentifier,
        @CustomerName nvarchar(100),
        @SalesPerson nvarchar(100),
        @CustomerType nvarchar(100),
        @sync_min_timestamp bigint ,                                
        @sync_row_count int OUT,
        @sync_force_write int)        
AS      
    UPDATE c
    SET c.CustomerName = @CustomerName, c.SalesPerson = @SalesPerson, c.CustomerType = @CustomerType      
    FROM Sales.Customer c JOIN Sync.Customer_Tracking t ON c.CustomerId = t.CustomerId
    WHERE ((t.local_update_peer_timestamp <= @sync_min_timestamp) OR @sync_force_write = 1)
        AND t.CustomerId = @CustomerId  
    SET @sync_row_count = @@rowcount

用于 UpdateMetadataCommand 的过程

create procedure Sync.sp_Customer_UpdateMetadata (
        @CustomerId uniqueidentifier,
        @sync_scope_local_id int,
        @sync_row_is_tombstone int,
        @sync_create_peer_key int,
        @sync_create_peer_timestamp bigint,                 
        @sync_update_peer_key int,
        @sync_update_peer_timestamp timestamp,                      
        @sync_row_timestamp timestamp,
        @sync_check_concurrency int,        
        @sync_row_count int out)        
as  
    declare @was_tombstone int
    select @was_tombstone = sync_row_is_tombstone from Sync.Customer_Tracking 
    where CustomerId = @CustomerId
    
    if (@was_tombstone is not null and @was_tombstone=1 and @sync_row_is_tombstone=0)
        -- tombstone is getting resurrected, update creation version as well
        update Sync.Customer_Tracking set
            [update_scope_local_id] = @sync_scope_local_id, 
            [scope_update_peer_key] = @sync_update_peer_key,
            [scope_update_peer_timestamp] = @sync_update_peer_timestamp,
            [local_update_peer_key] = 0,
            [restore_timestamp] = NULL,
            [create_scope_local_id] = @sync_scope_local_id, 
            [scope_create_peer_key] = @sync_create_peer_key, 
            [scope_create_peer_timestamp] =  @sync_create_peer_timestamp, 
            [sync_row_is_tombstone] = @sync_row_is_tombstone                        
        where CustomerId = @CustomerId          
        and (@sync_check_concurrency = 0 or local_update_peer_timestamp = @sync_row_timestamp)
    else    
        update Sync.Customer_Tracking set
            [update_scope_local_id] = @sync_scope_local_id, 
            [scope_update_peer_key] = @sync_update_peer_key,
            [scope_update_peer_timestamp] = @sync_update_peer_timestamp,
            [local_update_peer_key] = 0,
            [restore_timestamp] = NULL,
            [sync_row_is_tombstone] = @sync_row_is_tombstone                        
        where CustomerId = @CustomerId          
        and (@sync_check_concurrency = 0 or local_update_peer_timestamp = @sync_row_timestamp)
    set @sync_row_count = @@rowcount

用于 SelectRowCommand 的过程

create procedure Sync.sp_Customer_SelectRow
        @CustomerId uniqueidentifier,
        @sync_scope_local_id int
as
    select  t.CustomerId, 
            c.CustomerName,
            c.SalesPerson,
            c.CustomerType, 
            t.sync_row_is_tombstone,
            t.local_update_peer_timestamp as sync_row_timestamp, 
            case when (t.update_scope_local_id is null or t.update_scope_local_id <> @sync_scope_local_id) 
                 then case when (t.restore_timestamp is null) then t.local_update_peer_timestamp else t.restore_timestamp end else t.scope_update_peer_timestamp end as sync_update_peer_timestamp,
            case when (t.update_scope_local_id is null or t.update_scope_local_id <> @sync_scope_local_id) 
                 then t.local_update_peer_key else t.scope_update_peer_key end as sync_update_peer_key,
            case when (t.create_scope_local_id is null or t.create_scope_local_id <> @sync_scope_local_id) 
                 then t.local_create_peer_timestamp else t.scope_create_peer_timestamp end as sync_create_peer_timestamp,
            case when (t.create_scope_local_id is null or t.create_scope_local_id <> @sync_scope_local_id) 
                 then t.local_create_peer_key else t.scope_create_peer_key end as sync_create_peer_key
    from Sales.Customer c right join Sync.Customer_Tracking t on c.CustomerId = t.CustomerId    
    where c.CustomerId = @CustomerId 

用于 SelectMetadataForCleanupCommand 的过程

CREATE PROCEDURE Sync.sp_Customer_SelectMetadata     
    @metadata_aging_in_days int,
    @sync_scope_local_id int
AS
    IF @metadata_aging_in_days = -1
        BEGIN
            SELECT  CustomerId,
                    local_update_peer_timestamp as sync_row_timestamp,  
                    case when (update_scope_local_id is null or update_scope_local_id <> @sync_scope_local_id) 
                        then case when (restore_timestamp is null) then local_update_peer_timestamp else restore_timestamp end else scope_update_peer_timestamp end as sync_update_peer_timestamp,
                    case when (update_scope_local_id is null or update_scope_local_id <> @sync_scope_local_id) 
                        then local_update_peer_key else scope_update_peer_key end as sync_update_peer_key,
                    case when (create_scope_local_id is null or create_scope_local_id <> @sync_scope_local_id) 
                        then local_create_peer_timestamp else scope_create_peer_timestamp end as sync_create_peer_timestamp,
                    case when (create_scope_local_id is null or create_scope_local_id <> @sync_scope_local_id) 
                        then local_create_peer_key else scope_create_peer_key end as sync_create_peer_key
            FROM Sync.Customer_Tracking
            WHERE sync_row_is_tombstone = 1
        END
    
    ELSE
        BEGIN
            SELECT  CustomerId,
                    local_update_peer_timestamp as sync_row_timestamp,  
                    case when (update_scope_local_id is null or update_scope_local_id <> @sync_scope_local_id) 
                        then case when (restore_timestamp is null) then local_update_peer_timestamp else restore_timestamp end else scope_update_peer_timestamp end as sync_update_peer_timestamp,
                    case when (update_scope_local_id is null or update_scope_local_id <> @sync_scope_local_id) 
                        then local_update_peer_key else scope_update_peer_key end as sync_update_peer_key,
                    case when (create_scope_local_id is null or create_scope_local_id <> @sync_scope_local_id) 
                        then local_create_peer_timestamp else scope_create_peer_timestamp end as sync_create_peer_timestamp,
                    case when (create_scope_local_id is null or create_scope_local_id <> @sync_scope_local_id) 
                        then local_create_peer_key else scope_create_peer_key end as sync_create_peer_key
            FROM Sync.Customer_Tracking
            WHERE sync_row_is_tombstone = 1 AND
            DATEDIFF(day, last_change_datetime, GETDATE()) > @metadata_aging_in_days
        END

结论

本主题演练如何为变更跟踪设置数据库。设置某一数据库后,该数据库可与其他节点同步。有关如何配置和执行同步的更多信息,请参见以下主题:如何配置和执行协作同步(非 SQL Server)

请参阅

概念

同步其他与 ADO.NET 兼容的数据库
如何配置和执行协作同步(非 SQL Server)