交易型文章 - 指定變更如何傳遞

適用於:SQL ServerAzure SQL 受控執行個體

異動複寫可讓您指定資料變更從「發行者」傳播到「訂閱者」的方式。 對於每個已發佈的資料表,你可以指定每個操作(INSERT、UPDATE 或 DELETE)應以四種方式之一傳播至訂閱者:

  • 指定異動複寫應先編寫指令碼,然後呼叫預存程序,以將變更傳播至「訂閱者」(預設值)。

  • 指定變更應透過 INSERT、 UPDATE、 或 DELETE 陳述式來傳播(非 SQL Server 訂閱者的預設格式)。

  • 指定使用自訂預存程序。

  • 指定此動作不應在任何「訂閱者」處執行。 不會重複該類型的交易。

依預設,異動複寫會透過每個訂閱者上所安裝的一組預存程序,將變更傳播到訂閱者。 在「發行者」端的資料表中進行插入、更新或刪除操作時,作業會翻譯為對「訂閱者」端預存程序的呼叫。 儲存程序接受對應到資料表欄位的參數,允許用戶在訂閱者端變更這些欄位。

若要設定對交易式發行項之資料變更的傳播方法,請參閱< 設定對交易式發行項之資料變更的傳播方法>。

預設與自訂預存程序

複寫為每個資料表文章建立三個預設的儲存程序:

  • sp_MSins_<tablename>,負責處理插入。

  • 負責處理更新的 sp_MSupd_<tablename>。

  • 用於處理刪除的 sp_MSdel_<tablename>。

<程序中使用的表格名稱>取決於你如何將文章加入出版品,以及訂閱資料庫是否包含同名但擁有者不同的資料表。

你可以用在將文章新增至發行集時所指定的自訂程序,取代這些程序中的任何一個。 如果您的應用程式需要自訂邏輯,例如在訂閱者更新資料列時,將資料插入稽核表,請使用自訂程序。 欲了解更多關於指定自訂儲存程序的資訊,請參閱前一節列出的操作相關文章。

當你指定預設複製程序或自訂程序時,也會為每個程序指定呼叫語法。 如果你使用預設程序,複製會選擇預設呼叫語法。 呼叫語法決定提供給程序的參數結構,以及每個資料變更傳送到「訂閱者」的資訊量。 欲了解更多資訊,請參閱本文中的「呼叫儲存程序語法」章節。

使用自訂儲存程序的考量

使用自訂預存程序時,請記住下列考量:

  • 你必須在儲存程序中支援邏輯;Microsoft 不支援自訂邏輯。

  • 為避免與複製所用的交易衝突,請不要在自訂程序中使用明確的交易。

  • 訂閱者的結構通常與 Publisher 的結構相同,但若使用欄位篩選,它也可以是 Publisher 架構的子集。 如果你需要隨著資料移動轉換結構,使訂閱者的結構不再是 Publisher 架構的子集,可以使用 SQL Server 2019 整合服務(SSIS)。 如需詳細資訊,請參閱 SQL Server Integration Services。

  • 如果你對已發佈的資料表做結構變更,請重新產生自訂程序。 如需詳細資訊,請參閱重新產生自訂交易程序以反映結構描述變更。

  • 如果你在 散發代理程式 的 -SubscriptionStreams 參數中使用大於 1 的值,請確保主鍵欄位的更新成功。 例如:

    update ... set pk = 2 where pk = 1 -- update 1  
    update ... set pk = 3 where pk = 2 -- update 2  
    

    如果 散發代理程式 使用多個連線,這兩次更新可能會在不同的連線上重複發生。 如果先套用更新 1,就沒問題。 如果先套用更新 2,則會回傳 0 rows affected ,因為更新 1 尚未發生。 預設程序會處理此情況,若更新時沒有列受影響,則會產生錯誤:

    if @@rowcount = 0  
        if @@microsoftversion>0x07320000  
            exec sys.sp_MSreplraiserror 20598  
    

    提出錯誤會迫使 散發代理程式 在單一連線上重試更新,且成功。 自訂預存程序必須包括類似的邏輯。

預存程序的呼叫語法

你可以使用五種不同的語法選項來呼叫交易複製所使用的程序:

  • CALL 語法。 使用此語法進行插入、更新與刪除。 依預設,複寫使用此語法進行插入和刪除。

  • SCALL 語法。 此語法僅用於更新。 依預設,複寫使用此語法進行更新。

  • MCALL 語法。 此語法僅用於更新。

  • XCALL 語法。 更新和刪除時請使用此語法。

  • VCALL。 可更新訂閱請使用此語法。 僅供內部使用。

每種方法傳送給訂閱者的資料量各不相同。 例如,SCALL 只傳遞更新實際影響的欄位值。 XCALL 需要所有欄位,不論更新是否影響它們,以及每欄所有舊資料值。 在許多情況下,SCALL 適合用於更新,但若您的應用程式在更新時需要所有資料值,XCALL 則支援此需求。

CALL 語法

INSERT 預存程序
處理 INSERT 語句的儲存程序會接收所有欄位插入的值:

c1, c2, c3,... cn  

UPDATE 預存程序
處理 UPDATE 語句的儲存程序會接收文章中定義的所有欄位的更新值,接著是主鍵欄位的原始值。 此過程不嘗試判斷哪些欄位被更改:

c1, c2, c3,... cn, pkc1, pkc2, pkc3,... pkcn  

DELETE 預存程序
處理 DELETE 敘述的儲存程序會接收主鍵欄位的值:

pkc1, pkc2, pkc3,... pkcn  

SCALL 語法

UPDATE 預存程序
處理 UPDATE 敘述的儲存程序只會接收變更欄位的更新值,接著是主鍵欄位的原始值,然後是位元遮罩binary(n)()參數,指示變更欄位。 以下範例中,第 2 欄(c2)未改變:

c1, , c3,... cn, pkc1, pkc2, pkc3,... pkcn, bitmask  

MCALL 語法

UPDATE 預存程序
處理 UPDATE 語句的儲存程序會接收文章中定義的所有欄位的更新值,接著是主鍵欄位的原始值,然後是一個位元遮罩(binary(n))參數,指示變更的欄位:

c1, c2, c3,... cn, pkc1, pkc2, pkc3,... pkcn, bitmask  

XCALL 語法

UPDATE 預存程序
處理 UPDATE 敘述的儲存程序會接收文章中定義的所有欄位的原始值(前置影像),接著是文章中定義的所有欄位的更新值(後置影像):

old-c1, old-c2, old-c3,... old-cn, c1, c2, c3,... cn,  

DELETE 預存程序
處理 DELETE 語句的儲存程序會接收文章中定義的所有欄位的原始值(之前的圖片):

old-c1, old-c2, old-c3,... old-cn  

註釋

當您使用 XCALL 時,image 和 text 欄位的變更前映像值應為 NULL。

範例

下列程序是為 Adventure Works 範例資料庫中的 Vendor Table 建立的預設程序。

--INSERT procedure using CALL syntax  
create procedure [sp_MSins_PurchasingVendor]   
  @c1 int,@c2 nvarchar(15),@c3 nvarchar(50),@c4 tinyint,@c5 bit,@c6 bit,@c7 nvarchar(1024),@c8 datetime  
as   
begin   
insert into [Purchasing].[Vendor]([VendorID]  
,[AccountNumber]  
,[Name]  
,[CreditRating]  
,[PreferredVendorStatus]  
,[ActiveFlag]  
,[PurchasingWebServiceURL]  
,[ModifiedDate])  
values (   
 @c1  
,@c2  
,@c3  
,@c4  
,@c5  
,@c6  
,@c7  
,@c8  
 )   
end  
go  
  
--UPDATE procedure using SCALL syntax  
create procedure [sp_MSupd_PurchasingVendor]   
 @c1 int = null,@c2 nvarchar(15) = null,@c3 nvarchar(50) = null,@c4 tinyint = null,@c5 bit = null,@c6 bit = null,@c7 nvarchar(1024) = null,@c8 datetime = null,@pkc1 int  
,@bitmap binary(2)  
as  
begin  
update [Purchasing].[Vendor] set   
 [AccountNumber] = case substring(@bitmap,1,1) & 2 when 2 then @c2 else [AccountNumber] end  
,[Name] = case substring(@bitmap,1,1) & 4 when 4 then @c3 else [Name] end  
,[CreditRating] = case substring(@bitmap,1,1) & 8 when 8 then @c4 else [CreditRating] end  
,[PreferredVendorStatus] = case substring(@bitmap,1,1) & 16 when 16 then @c5 else [PreferredVendorStatus] end  
,[ActiveFlag] = case substring(@bitmap,1,1) & 32 when 32 then @c6 else [ActiveFlag] end  
,[PurchasingWebServiceURL] = case substring(@bitmap,1,1) & 64 when 64 then @c7 else [PurchasingWebServiceURL] end  
,[ModifiedDate] = case substring(@bitmap,1,1) & 128 when 128 then @c8 else [ModifiedDate] end  
where [VendorID] = @pkc1  
if @@rowcount = 0  
    if @@microsoftversion>0x07320000  
        exec sp_MSreplraiserror 20598  
end  
go  
  
--DELETE procedure using CALL syntax  
create procedure [sp_MSdel_PurchasingVendor]   
  @pkc1 int  
as   
begin   
delete [Purchasing].[Vendor]  
where [VendorID] = @pkc1  
if @@rowcount = 0  
    if @@microsoftversion>0x07320000  
        exec sp_MSreplraiserror 20598  
end   
go