共用方式為


HOW TO:使用自訂變更追蹤系統

許多應用程式都會要求在伺服器資料庫內追蹤變更,以便在後續的同步處理工作階段期間,將這些變更傳遞到用戶端。本主題描述變更追蹤系統的需求,並示範如何建立 Sync Framework 可以使用的自訂系統。自訂變更追蹤適用於某些情況,但是,請注意自訂變更追蹤確實會帶來一些複雜性,並影響伺服器資料庫效能。如果是使用 SQL Server 2008,建議您使用 SQL Server 變更追蹤功能。如需詳細資訊,請參閱 HOW TO:使用 SQL Server 變更追蹤

同步處理案例的伺服器需求

Sync Framework 的設計目的,是要對伺服器資料庫的影響降到最低。因此,伺服器資料庫中變更追蹤所需的修改,與您對應用程式的功能需求層級成正比,請記住以下考量:

  • 在光譜的一端是僅限下載的資料快照集 (Snapshot)。它不需要任何變更。

  • 而在光譜的另一端,則是使用完整變更追蹤和衝突偵測的雙向同步處理。

下表摘要說明您可以使用 Sync Framework 的方式,並為伺服器資料庫識別對應的需求。

情節 主索引鍵或唯一的資料行 1 追蹤更新時間 追蹤插入時間 追蹤刪除時間 追蹤用戶端 ID 的更新 追蹤用戶端 ID 的插入 追蹤用戶端 ID 的刪除

下載資料快照集至用戶端。

下載累加插入和更新至用戶端。

是 2

下載累加插入、更新和刪除至用戶端。

是 2

上傳插入到伺服器。

否 3

上傳插入和更新到伺服器。

否 3

否 3

上傳插入、更新和刪除到伺服器。

否 3

否 3

否 3

雙向插入和更新衝突偵測。

是 2

是 4

是 4

雙向插入、更新和刪除衝突偵測。

是 2

是 4

是 4

是 4

1 主索引鍵在所有節點之間都必須是唯一的,而且不能重複使用:如果刪除某個資料列,該資料列的主索引鍵不應該用於另一個資料列。一般來說,識別資料行並不是分散式環境的適當選擇。如需主索引鍵的詳細資訊,請參閱為分散式環境選取適當的主索引鍵

2 如果您想要區分插入和更新,則需要此項。如需詳細資訊,請參閱本主題稍後的「判斷要將哪些資料變更下載到用戶端」一節。

3 如果超過一個用戶端可能要變更資料列,而您想要識別哪個用戶端進行了變更,則需要此項。如需詳細資訊,請參閱本主題稍後的「識別哪個用戶端進行了資料變更」一節。

4 如果您不想讓變更回應到進行該變更的用戶端,則需要此項。如需詳細資訊,請參閱本主題稍後的「識別哪個用戶端進行了資料變更」一節。

注意

除了先前所描述的變更之外,您可能也會想為資料存取建立預存程序。大部分在此文件中的範例使用內嵌 SQL,因為比較容易顯示出在程式碼之內發生了什麼。而在實際執行應用程式中,則應該基於下列原因使用預存程序:如果正確編寫的話,預存程式可以封裝程式碼、有較佳的執行效率,而且能夠提供比內嵌 SQL 更好的安全性。

判斷要將哪些資料變更下載到用戶端

在僅限下載及雙向的同步處理中,您必須在伺服器上追蹤變更,如此一來 Sync Framework 才能判斷要將哪些變更下載到用戶端。雖然 Sync Framework 不會特別定義要怎麼支援變更追蹤,但仍有其常用的方式。針對每個要進行同步的資料表,您可以採用下列方式:

  • 加入追蹤資料列是何時插入伺服器資料庫的資料行。

  • 加入資料行或者有時是觸發程序 (Trigger),追蹤資料列何時在伺服器資料庫中進行最後更新。

  • 加入「標記資料表」(Tombstone Table) 及觸發程序 (Trigger),可追蹤資料列何時從伺服器資料庫中刪除。如果您不想從伺服器刪除資料,但卻想將刪除傳送到用戶端,您可以在基底資料表中追蹤邏輯刪除:使用資料行 (通常是 bit 型別),表示已刪除某個資料列,並使用另一個資料行追蹤刪除是在何時發生。

這些資料行和標記資料表會搭配「錨定」(Anchor) 一起使用,以決定要下載哪些插入、更新及刪除。錨定是一個時間點,用以定義一組要進行同步的變更。請考量下列查詢:

  • 您為 SelectIncrementalInsertsCommand 屬性所指定的查詢。此查詢會從 Sync Framework 範例資料庫的 Sales.Customer 資料表中下載累加插入,如下所示:

    SELECT CustomerId, CustomerName, SalesPerson, CustomerType FROM
    Sales.Customer WHERE InsertTimestamp > @sync_last_received_anchor
    AND InsertTimestamp <= @sync_new_received_anchor
    

    如需有關此屬性和其他與同步處理命令有關的屬性之詳細資訊,請參閱 HOW TO:指定快照集、下載、上傳及雙向同步處理

  • 您為 SelectNewAnchorCommand 屬性所指定的查詢。這個查詢會擷取一個時間點值。InsertTimestamp 資料行會儲存時間戳記值。因此,此查詢使用在 SQL Server 2005 Service Pack 2 中引入的 Transact-SQL MIN_ACTIVE_ROWVERSION 函式,從伺服器資料庫中擷取時間戳記值,如下所示:

    SELECT @sync_new_received_anchor = MIN_ACTIVE_ROWVERSION - 1
    

    MIN_ACTIVE_ROWVERSION 傳回目前資料庫中最低的使用中 timestamp (也稱為 rowversion) 值。如果 timestamp 值使用在尚未認可的交易中,則其值為使用中。如果在資料庫中沒有使用中的值,MIN_ACTIVE_ROWVERSION 會傳回與 @@DBTS + 1 相同的值。例如,對於使用 timestamp 值為一個群組集的變更一起進行資料同步處理的情節來說,MIN_ACTIVE_ROWVERSION 是有用的。如果應用程式在其錨定命令中使用 @@DBTS 而非 MIN_ACTIVE_ROWVERSION,有可能會遺漏在同步處理發生時使用中的變更。

Sales.Customer 資料表第一次進行同步時,會發生下列程序:

  1. 新的錨定命令會執行。命令傳回值 0x0000000000000D49。這個值會儲存在用戶端資料庫。資料表從未進行過同步處理,因此,不會有來自先前同步處理的錨定值儲存在用戶端資料表中。在這個案例中,Sync Framework 會使用 SQL Server timestamp 資料型別所能使用的最低值:0x0000000000000000。Sync Framework 執行的查詢如下所示。這個查詢會從資料表下載結構描述和所有資料列。

    exec sp_executesql N'SELECT CustomerId, CustomerName, SalesPerson,
    CustomerType FROM Sales.Customer WHERE (InsertTimestamp >
    @sync_last_received_anchor AND InsertTimestamp <=
    @sync_new_received_anchor)',N'@sync_last_received_anchor timestamp,
    @sync_new_received_anchor timestamp',
    @sync_last_received_anchor=0x0000000000000000,
    @sync_new_received_anchor=0x0000000000000D49
    
  2. 在第二次同步處理期間,系統會執行新的錨定命令。自上次同步處理後,系統已插入資料列。因此,此命令會傳回值 0x0000000000000D4C。資料表從前進行過同步處理。因此,Sync Framework 可以擷取錨點值 0x0000000000000D49。這個值會儲存在先前同步處理的用戶端資料庫中。執行的查詢如下所示。此查詢只從資料表下載在兩個錨定值之間插入的資料列。

    exec sp_executesql N'SELECT CustomerId, CustomerName, SalesPerson,
    CustomerType FROM Sales.Customer WHERE (InsertTimestamp >
    @sync_last_received_anchor AND InsertTimestamp <=
    @sync_new_received_anchor)', N'@sync_last_received_anchor timestamp,
    @sync_new_received_anchor timestamp',
    @sync_last_received_anchor=0x0000000000000D49,
    @sync_new_received_anchor=0x0000000000000D4C
    

如需更新和刪除命令的範例,請參閱 HOW TO:下載累加資料變更至用戶端HOW TO:交換用戶端和伺服器之間的雙向增量資料變更

如先前所提及的,用於擷取錨定值的命令是根據伺服器資料庫中追蹤資料行的型別而定。在此文件中的範例使用 SQL Server timestamp,也稱為 rowversion。若要使用 SQL Server datetime 資料行,新的錨定命令的查詢會與下列所述相似:

SELECT @sync_new_received_anchor = GETUTCDATE()

若要判斷應該針對某個錨定使用哪個資料型別,您應該權衡應用程式需求,並考量您在變更伺服器資料庫結構描述方面有多大的彈性。如果資料庫仍在開發中,您可以明確地指定要加入哪個資料行和觸發程式。如果資料庫已經在實際執行中,可行的選項就會受到比較大的限制。請考量下列方針:

  • 所有在同步處理群組中的資料表應該使用相同的資料型別及新的錨定命令。如果可以的話,請在所有群組中使用相同的資料型別。

  • datetime 資料型別易於了解,而且資料表中通常已包含了會追蹤資料列何時進行修改的資料行。然而,如果用戶端處在不同的時區中,此資料型別可能會有問題。如果您使用此資料型別,當選取累加變更時可能會遺漏交易。

  • timestamp 資料型別更為精確,而且不是以時間為基礎。不過,在 SQL Server 資料庫中的每個資料表只能包含一個此資料型別的資料行。因此,如果您必須區分插入和更新,您可以加入不同資料型別的資料行,例如 binary(8),並將時間戳記值儲存在該資料行中。如需範例,請參閱 資料庫提供者的安裝指令碼 HOW-TO 主題。如果已從備份中還原伺服器資料庫,timestamp 可能會是一個問題。如需詳細資訊,請參閱Sync Framework 支援的資料庫物件。如先前所提及,我們建議您在選取新的錨定的命令中使用 MIN_ACTIVE_ROWVERSION。

識別哪個用戶端進行了資料變更

要識別哪個用戶端進行了資料變更,有兩個主要的原因:

  • 為了在僅限上傳和雙向的同步處理中支援衝突偵測與解析。

    如果能夠變更某個特定資料行的是伺服器和用戶端,或者超過一個用戶端能夠對資料行進行變更,您可能會想要識別出這個變更究竟是在哪裡進行的。此資訊讓您能夠編寫程式碼,例如在某個變更和另一個變更之間設出優先順序。如果沒有這個資訊,最後一個對資料行進行的變更就會存留下來。

  • 為了防止變更在雙向同步處理中回應至用戶端。

    Sync Framework 會先將變更上傳至伺服器,然後下載變更至用戶端。如果您沒有追蹤進行變更的用戶端的識別 (Identity),該變更會上傳到伺服器,然後在同一個同步處理工作階段中下載回用戶端。在某些案例中,這樣的變更回應是可以接受的,但在其他案例中可能就不行了。

如同變更追蹤,Sync Framework 不會特別定義要怎麼支援識別追蹤,但仍有其常用的方式。針對每個要進行同步的資料表,您可以採用下列方式:

  • 在追蹤由何者進行每個插入的基底資料表裡加入資料行。

  • 在追蹤由何者進行每個更新的基底資料表裡加入資料行。

  • 在追蹤由何者進行每個刪除的標記資料表裡加入資料行。

這些資料行和資料表要搭配 ClientId 屬性使用,以判斷哪個用戶端進行了哪些插入、更新或刪除。在任何資料表第一次使用除了快照集同步處理之外的方法進行同步時,Sync Framework 就會在用戶端上儲存 GUID 值,以識別該用戶端。此 ID 會傳遞至 DbServerSyncProvider,這樣一來每個 SyncAdapter 中選取和更新的查詢就能夠使用它了。此 ID 值可透過 ClientId 屬性取得。請考量下列 Transact-SQL 查詢:

SELECT CustomerId, CustomerName, SalesPerson, CustomerType FROM
Sales.Customer WHERE InsertTimestamp > @sync_last_received_anchor AND
InsertTimestamp <= @sync_new_received_anchor AND InsertId <>
@sync_client_id

此查詢類似於先前追蹤伺服器上進行之插入的查詢。在 WHERE 子句中的陳述式,確保系統只會下載並非由正在同步的用戶端所建立的插入。

Sync Framework 也能讓應用程式在伺服器上使用整數而非 GUID 值來識別用戶端。如需詳細資訊,請參閱 HOW TO:使用工作階段變數

伺服器準備範例

下列範例會示範如何從 Sync Framework 範例資料庫設定 Sales.Customer 資料表,並包含可處理最複雜之應用程式案例的追蹤基礎結構:包含衝突偵測的雙向插入、更新和刪除作業。沒有那麼複雜的情節就不需要整個基礎作業。如需詳細資訊,請參閱本主題先前的「同步處理準備情節的伺服器需求」一節。如需在此範例中用來建立物件的完整指令碼及其他物件,請參閱資料庫提供者的安裝指令碼 HOW-TO 主題。如需有關如何使用這些屬性的詳細資訊,請參閱 HOW TO:指定快照集、下載、上傳及雙向同步處理

本節中的範例進行下列步驟準備伺服器:

  1. 確認 Sales.Customer 結構描述。判斷資料表是否有主索引鍵,以及是否有任何資料行可以用在變更追蹤上。

  2. 加入資料行,以追蹤插入和變更進行的時間和位置。

  3. 建立標記資料表,並加入觸發程序到 Sales.Customer 資料表,以填入該資料表。

確認 Sales.Customer 結構描述

下列程式碼範例會示範 Sales.Customer 資料表的結構描述。資料表在 CustomerId 資料行中有主索引鍵,並且沒有可以用在變更追蹤上的資料行。

CREATE TABLE SyncSamplesDb.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)

加入資料行追蹤插入和更新作業

下列程式碼範例會加入四個資料行:UpdateTimestampInsertTimestampUpdateIdInsertIdUpdateTimestamp 資料行屬於 SQL Server timestamp 資料行。當資料列更新時,此資料行也會自動更新。如同先前曾提及的,一個資料表只能有一個 timestamp 資料行。因此,InsertTimestamp 資料行是預設為 @@DBTS + 1binary(8) 資料行。此範例會加入由 @@DBTS 傳回的值,因此 UpdateTimestampInsertTimestamp 資料行在執行插入後會有相同的值。如果上述步驟沒有完成,看來會像是每個資料列在插入後才進行更新。

Sync Framework 為每個用戶端所建立的 ID 是 GUID,因此,兩個 ID 資料行就是 uniqueidentifier 資料行。資料行預設值為 00000000-0000-0000-0000-000000000000。此值表示伺服器已進行過更新或插入。下列範例包含在標記資料表中的 DeleteId 資料行。

ALTER TABLE SyncSamplesDb.Sales.Customer 
    ADD UpdateTimestamp timestamp
ALTER TABLE SyncSamplesDb.Sales.Customer 
    ADD InsertTimestamp binary(8) DEFAULT @@DBTS + 1
ALTER TABLE SyncSamplesDb.Sales.Customer 
    ADD UpdateId uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'
ALTER TABLE SyncSamplesDb.Sales.Customer 
    ADD InsertId uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'

現在系統已經加入了資料行,而下列範例程式碼則會加入索引。在範例程式碼中的這些和其他的索引,都是在同步處理中查詢的資料行所建立的。加入索引的目的,是為了強調在伺服器資料庫上實作變更追蹤時,您應該考量這些索引。請確認您在伺服器的效能與同步處理的效能之間已做了權衡。

CREATE NONCLUSTERED INDEX IX_Customer_UpdateTimestamp
ON Sales.Customer(UpdateTimestamp)

CREATE NONCLUSTERED INDEX IX_Customer_InsertTimestamp
ON Sales.Customer(InsertTimestamp)

CREATE NONCLUSTERED INDEX IX_Customer_UpdateId
ON Sales.Customer(UpdateId)

CREATE NONCLUSTERED INDEX IX_Customer_InsertId
ON Sales.Customer(InsertId)

加入標記資料表追蹤刪除作業

下列程式碼範例會建立以叢集索引及觸發程序填入其中的標記資料表。當刪除作業在 Sales.Customer 資料表中發生時,觸發程序會在 Sales.Customer_Tombstone 資料表中插入資料列。在觸發程序執行插入作業之前,它會檢查 Sales.Customer_Tombstone 資料表是否已包含有刪除資料列主索引鍵的資料列。它的發生條件是:當系統從 Sales.Customer 中刪除資料列、重新插入,並再次刪除該資料列時。如果在 Sales.Customer_Tombstone 裡偵測到這樣的資料列,觸發程式會刪除該資料列並重新插入它。Sales.Customer_Tombstone 中的 DeleteTimestamp 資料行也會更新。

CREATE TABLE SyncSamplesDb.Sales.Customer_Tombstone(
    CustomerId uniqueidentifier NOT NULL PRIMARY KEY NONCLUSTERED, 
    CustomerName nvarchar(100) NOT NULL,
    SalesPerson nvarchar(100) NOT NULL,
    CustomerType nvarchar(100) NOT NULL,
    DeleteId uniqueidentifier NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000',
    DeleteTimestamp timestamp)

CREATE TRIGGER Customer_DeleteTrigger 
ON SyncSamplesDb.Sales.Customer FOR DELETE 
AS 
BEGIN 
    SET NOCOUNT ON
    DELETE FROM SyncSamplesDb.Sales.Customer_Tombstone 
        WHERE CustomerId IN (SELECT CustomerId FROM deleted)
    INSERT INTO SyncSamplesDb.Sales.Customer_Tombstone (CustomerId, CustomerName, SalesPerson, CustomerType) 
    SELECT CustomerId, CustomerName, SalesPerson, CustomerType FROM deleted
    SET NOCOUNT OFF
END

CREATE CLUSTERED INDEX IX_Customer_Tombstone_DeleteTimestamp
ON Sales.Customer_Tombstone(DeleteTimestamp)

CREATE NONCLUSTERED INDEX IX_Customer_Tombstone_DeleteId
ON Sales.Customer_Tombstone(DeleteId)

請參閱

概念

追蹤伺服器資料庫中的變更