適用於:SQL Server
本主題說明如何使用 SQL Server Management Studio、Transact-SQL 或 Replication Management Objects (RMO),在 SQL Server 中定義合併資料表發行項之間的邏輯記錄關聯性。
合併式複寫可讓您在不同資料表內定義相關資料列之間的關聯性。 接著,這些資料列可在同步期間作為一個交易單位來處理。 可以在兩個發行項之間定義邏輯記錄,不論這些發行項是否有聯結篩選關聯性。 如需詳細資訊,請參閱使用邏輯記錄將相關資料列變更分組。
注意
SQL Server 的未來版本將移除此功能。 請避免在新的開發工作中使用這項功能,並規劃修改目前使用這項功能的應用程式。
限制事項
- 如果您在初始化發行集的訂閱後,新增、修改或刪除邏輯記錄,則必須在進行變更後產生新的快照集並重新初始化所有訂閱。 如需屬性變更需求的詳細資訊,請參閱變更發行集與發行項屬性。
使用 SQL Server Management Studio
在可於 [新增發行集精靈] 和 [發行集屬性 - <發行集] 對話方塊中使用的 [新增聯結] 對話方塊中,定義邏輯記錄。 如需使用精靈及存取對話方塊的詳細資訊,請參閱建立發行集和檢視及修改發行集屬性。
只有將邏輯記錄套用至合併式發行集中的聯結篩選,並且該發行集符合使用預先計算的資料分割要求時,方可在 [加入聯結] 對話方塊中定義這些邏輯記錄。 若要定義未套用至聯結篩選的邏輯記錄,並在邏輯記錄層級設定衝突偵測和解決方案,您必須使用預存程序。
若要定義邏輯記錄關聯性
在 [新增發行集精靈] 的 [篩選資料表資料列] 頁面,或在 [發行集屬性 - 發行集] 對話方塊的 [篩選資料列] 頁面上,從 [已篩選的資料表] 窗格中選取資料列篩選。
邏輯記錄關聯性與聯結篩選相關聯,這會擴充資料列篩選。 因此,您必須先定義資料列篩選條件,才能使用聯結來擴充篩選條件,並套用邏輯記錄關係。 定義好一個聯結篩選後,您可以以另一個聯結篩選擴充這個聯結篩選。 如需定義聯結篩選的詳細資訊,請參閱< 定義和修改合併發行項之間的聯結篩選>。
按一下 [加入] ,然後按一下 [加入聯結以擴充選取的篩選] 。
在 [加入聯結] 對話方塊中定義聯結篩選,然後選取 [邏輯記錄] 核取方塊。
如果您位於 出版物屬性 - <出版物> 對話方塊中,請按一下 確定 以儲存並關閉對話方塊。
若要刪除邏輯記錄關聯
僅刪除邏輯記錄關聯性,或刪除邏輯記錄關聯性及與其相關聯的聯結篩選。
若要僅刪除邏輯記錄關聯性:
在篩選資料列頁面上(位於 [新增發行精靈] 中),或在篩選資料列頁面上(位於發行屬性 - 發行對話方塊中),於已篩選資料表窗格中選取與邏輯記錄關聯相關的聯結篩選,然後按一下編輯。
在 [編輯聯結] 對話方塊中,清除 [邏輯記錄] 核取方塊。
選取 OK。
若要刪除邏輯記錄關聯及其相關的聯結篩選條件:
- 在 [新增發行集精靈] 或 [發行集屬性 - <發行集] 對話方塊的 [篩選資料列] 頁面上,於 [已篩選的資料表] 窗格中選取篩選,然後按一下 [刪除]。 如果您刪除的聯結篩選本身已由其他聯結擴充,也會一併刪除這些聯結。
使用 TRANSACT-SQL
您可以使用複寫預存程序,以程式設計方式指定發行項之間的邏輯記錄關聯性。
定義不含相關聯結篩選條件的邏輯記錄關係
如果發行集包含任何篩選的發行項,請執行 sp_helpmergepublication,並記下結果集中的 use_partition_groups 值。
如果這個值是 1,則表示已經使用預先計算的資料分割。
如果這個值是 0,請在發行集資料庫的發行者上執行 sp_changemergepublication 。 請為 @property 指定 use_partition_groups 的值,並為 @value 指定 true的值。
注意
如果發行項目不支援預先計算的分割區,則無法使用邏輯記錄。 如需詳細資訊,請參閱使用預先計算的資料分割最佳化參數化篩選效能主題中的<使用預先計算的資料分割之需求>。
如果此值為 NULL,則需要執行快照代理程式,才能為發行產生初始快照。
如果將組成此邏輯記錄的文章不存在,請在發行者端的發行資料庫上執行sp_addmergearticle。 針對此邏輯記錄指定下列其中一個衝突偵測和解決選項:
若要偵測及解決邏輯記錄中相關資料列內所發生的衝突,請為 @logical_record_level_conflict_detection 和 @logical_record_level_conflict_resolution 指定 true的值。
若要使用標準資料列層級或資料行層級的衝突偵測與解決方法,請為 @logical_record_level_conflict_detection 和 @logical_record_level_conflict_resolution 指定 false的值 (這是預設值)。
對將構成此邏輯記錄的每個項目重複執行步驟 2。 您必須對邏輯記錄中的每篇文章使用相同的衝突偵測與解決選項。 如需詳細資訊,請參閱 偵測和解決邏輯記錄中的衝突。
在發行者上的發行資料庫中,執行 sp_addmergefilter。 指定 @publication、關聯中的其中一篇文章名稱作為 @article、第二篇文章名稱作為 @join_articlename、關聯名稱作為 @filtername、用於定義兩篇文章之間關聯的子句作為 @join_filterclause、聯結類型作為 @join_unique_key,以及下列其中一個值作為 @filter_type:
2 - 定義邏輯關聯性。
3 - 定義具有聯結篩選的邏輯關聯性。
注意
如果未使用聯結篩選,則兩個發行項之間的關聯性方向就不是那麼重要。
針對發佈內容中其餘每個邏輯記錄關係,重複執行步驟 2。
變更邏輯記錄的衝突偵測和解決方式
若要偵測及解決邏輯記錄中相關資料列內所發生的衝突:
在發行者上的發行資料庫中,執行 sp_changemergearticle。 請為 @property 指定 logical_record_level_conflict_detection 的值,並為 @value 指定 true的值。 為 @force_invalidate_snapshot 和 @force_reinit_subscription 指定 1的值。
在發行者上的發行資料庫中,執行 sp_changemergearticle。 請為 @property 指定 logical_record_level_conflict_resolution 的值,並為 @value 指定 true的值。 為 @force_invalidate_snapshot 和 @force_reinit_subscription 指定 1的值。
若要使用標準資料列層級或資料行層級的衝突偵測與解決方式:
在發行者上的發行資料庫中,執行 sp_changemergearticle。 請為 @property 指定 logical_record_level_conflict_detection 的值,並為 @value 指定 false的值。 為 @force_invalidate_snapshot 和 @force_reinit_subscription 指定 1的值。
在發行者上的發行資料庫中,執行 sp_changemergearticle。 請為 @property 指定 logical_record_level_conflict_resolution 的值,並為 @value 指定 false的值。 為 @force_invalidate_snapshot 和 @force_reinit_subscription 指定 1的值。
移除邏輯記錄關聯
在發行集資料庫的發行者上,執行下列查詢來傳回有關針對指定之發行集定義之所有邏輯記錄關聯性的資訊:
SELECT f.* FROM sysmergesubsetfilters AS f INNER JOIN sysmergepublications AS p ON f.pubid = p.pubid WHERE p.[name] = @publication;請注意結果集中 filtername 欄位內將被移除的邏輯記錄關聯名稱。
注意
此查詢會傳回與 sp_helpmergefilter相同的資訊,但是此系統預存程序只會傳回也屬於聯結篩選之邏輯記錄關聯性的相關資訊。
在發行資料庫的發行者端,執行 sp_dropmergefilter。 指定 @publication、@article 關聯中的其中一篇文章名稱,以及步驟 1 中 @filtername 的關聯名稱。
範例 (Transact-SQL)
這個範例會在現有的發行上啟用預先計算的資料分割功能,並為 SalesOrderHeader 和 SalesOrderDetail 資料表建立一筆包含兩個新發行項的邏輯記錄。
-- Remove ON DELETE CASCADE from FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID;
-- logical records cannot be used with ON DELETE CASCADE.
IF EXISTS (SELECT * FROM sys.objects
WHERE name = 'FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID')
BEGIN
ALTER TABLE [Sales].[SalesOrderDetail]
DROP CONSTRAINT [FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID]
END
ALTER TABLE [Sales].[SalesOrderDetail]
WITH CHECK ADD CONSTRAINT [FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID]
FOREIGN KEY([SalesOrderID])
REFERENCES [Sales].[SalesOrderHeader] ([SalesOrderID])
GO
DECLARE @publication AS sysname;
DECLARE @table1 AS sysname;
DECLARE @table2 AS sysname;
DECLARE @table3 AS sysname;
DECLARE @salesschema AS sysname;
DECLARE @hrschema AS sysname;
DECLARE @filterclause AS nvarchar(1000);
DECLARE @partitionoption AS bit;
SET @publication = N'AdvWorksSalesOrdersMerge';
SET @table1 = N'SalesOrderDetail';
SET @table2 = N'SalesOrderHeader';
SET @salesschema = N'Sales';
SET @hrschema = N'HumanResources';
SET @filterclause = N'Employee.LoginID = HOST_NAME()';
-- Ensure that the publication uses precomputed partitions.
SET @partitionoption = (SELECT [use_partition_groups] FROM sysmergepublications
WHERE [name] = @publication);
IF @partitionoption <> 1
BEGIN
EXEC sp_changemergepublication
@publication = @publication,
@property = N'use_partition_groups',
@value = 'true',
@force_invalidate_snapshot = 1;
END
-- Add a filtered article for the Employee table.
EXEC sp_addmergearticle
@publication = @publication,
@article = @table1,
@source_object = @table1,
@type = N'table',
@source_owner = @hrschema,
@schema_option = 0x0004CF1,
@description = N'article for the Employee table',
@subset_filterclause = @filterclause;
-- Add an article for the SalesOrderHeader table.
EXEC sp_addmergearticle
@publication = @publication,
@article = @table2,
@source_object = @table2,
@type = N'table',
@source_owner = @salesschema,
@schema_option = 0x0034EF1,
@description = N'article for the SalesOrderHeader table';
-- Add an article for the SalesOrderDetail table.
EXEC sp_addmergearticle
@publication = @publication,
@article = @table3,
@source_object = @table3,
@source_owner = @salesschema,
@description = 'article for the SalesOrderDetail table',
@identityrangemanagementoption = N'auto',
@pub_identity_range = 100000,
@identity_range = 100,
@threshold = 80;
-- Add a merge join filter between Employee and SalesOrderHeader.
EXEC sp_addmergefilter
@publication = @publication,
@article = @table2,
@filtername = N'SalesOrderHeader_Employee',
@join_articlename = @table1,
@join_filterclause = N'Employee.EmployeeID = SalesOrderHeader.SalesPersonID',
@join_unique_key = 1,
@filter_type = 1,
@force_invalidate_snapshot = 1,
@force_reinit_subscription = 1;
-- Create a logical record relationship that is also a merge join
-- filter between SalesOrderHeader and SalesOrderDetail.
EXEC sp_addmergefilter
@publication = @publication,
@article = @table3,
@filtername = N'LogicalRecord_SalesOrderHeader_SalesOrderDetail',
@join_articlename = @table2,
@join_filterclause = N'[SalesOrderHeader].[SalesOrderID] = [SalesOrderDetail].[SalesOrderID]',
@join_unique_key = 1,
@filter_type = 3,
@force_invalidate_snapshot = 1,
@force_reinit_subscription = 1;
GO
使用複寫管理物件 (RMO)
注意
合併式複寫可允許您指定在邏輯記錄層級追蹤及解決衝突,但這些選項無法使用 RMO 來設定。
定義不含相關聯結篩選條件的邏輯記錄關係
使用 ServerConnection 類別建立與發行者的連接。
建立 MergePublication 類別的執行個體、為發行集設定 Name 和 DatabaseName 屬性,以及將 ConnectionContext 屬性設定為步驟 1 中所建立的連接。
呼叫 LoadProperties 方法以取得物件的屬性。 如果此方法傳回 false,則表示步驟 2 中的發行集屬性定義不正確,或者該發行集不存在。
如果 PartitionGroupsOption 屬性設定為 False,請將它設定為 True。
如果將構成此邏輯記錄的各項目不存在,請建立 MergeArticle 類別的執行個體,並設定下列屬性:
Name 的文章名稱。
PublicationName 的發行集名稱。
(選擇性) 如果以水平方式篩選此發行項,請為 FilterClause 屬性指定資料列篩選子句。 使用此屬性可指定靜態或參數化資料列篩選器。 如需詳細資訊,請參閱< 參數化資料列篩選器>。
如需詳細資訊,請參閱 定義發行項。
呼叫 Create 方法。
對於構成該邏輯記錄的每個項目,重複執行步驟 5 和 6。
建立 MergeJoinFilter 類別的執行個體,以定義文章之間的邏輯記錄關聯。 然後,設定下列屬性:
ArticleName 屬性在邏輯記錄關聯中的子文章名稱。
JoinArticleName 屬性在邏輯記錄關聯中的現有父文章名稱。
FilterName 屬性的邏輯記錄關聯名稱。
定義 JoinFilterClause 屬性關聯性的運算式。
FilterTypes 屬性的值為 LogicalRecordLink。 如果邏輯記錄關聯性也是聯結篩選,請針對這個屬性指定 JoinFilterAndLogicalRecordLink 的值。 如需詳細資訊,請參閱使用邏輯記錄將相關資料列變更分組。
在代表關聯中子文章的物件上呼叫 AddMergeJoinFilter 方法。 傳遞步驟 8 中的 MergeJoinFilter 物件來定義關聯性。
對發行集中的其餘每個邏輯記錄關係重複執行步驟 8 和 9。
範例 (RMO)
此範例會針對 SalesOrderHeader 和 SalesOrderDetail 資料表建立一筆由兩個新項目組成的邏輯記錄。
// Define the Publisher and publication names.
string publisherName = publisherInstance;
string publicationName = "AdvWorksSalesOrdersMerge";
string publicationDbName = "AdventureWorks2022";
// Specify article names.
string articleName1 = "SalesOrderHeader";
string articleName2 = "SalesOrderDetail";
// Specify logical record information.
string lrName = "SalesOrderHeader_SalesOrderDetail";
string lrClause = "[SalesOrderHeader].[SalesOrderID] = "
+ "[SalesOrderDetail].[SalesOrderID]";
string schema = "Sales";
MergeArticle article1 = new MergeArticle();
MergeArticle article2 = new MergeArticle();
MergeJoinFilter lr = new MergeJoinFilter();
MergePublication publication = new MergePublication();
// Create a connection to the Publisher.
ServerConnection conn = new ServerConnection(publisherName);
try
{
// Connect to the Publisher.
conn.Connect();
// Verify that the publication uses precomputed partitions.
publication.Name = publicationName;
publication.DatabaseName = publicationDbName;
publication.ConnectionContext = conn;
// If we can't get the properties for this merge publication, then throw an application exception.
if (publication.LoadProperties())
{
// If precomputed partitions is disabled, enable it.
if (publication.PartitionGroupsOption == PartitionGroupsOption.False)
{
publication.PartitionGroupsOption = PartitionGroupsOption.True;
}
}
else
{
throw new ApplicationException(String.Format(
"Settings could not be retrieved for the publication. " +
"Ensure that the publication {0} exists on {1}.",
publicationName, publisherName));
}
// Set the required properties for the PurchaseOrderHeader article.
article1.ConnectionContext = conn;
article1.Name = articleName1;
article1.DatabaseName = publicationDbName;
article1.SourceObjectName = articleName1;
article1.SourceObjectOwner = schema;
article1.PublicationName = publicationName;
article1.Type = ArticleOptions.TableBased;
// Set the required properties for the SalesOrderDetail article.
article2.ConnectionContext = conn;
article2.Name = articleName2;
article2.DatabaseName = publicationDbName;
article2.SourceObjectName = articleName2;
article2.SourceObjectOwner = schema;
article2.PublicationName = publicationName;
article2.Type = ArticleOptions.TableBased;
if (!article1.IsExistingObject) article1.Create();
if (!article2.IsExistingObject) article2.Create();
// Define a logical record relationship between
// PurchaseOrderHeader and PurchaseOrderDetail.
// Parent article.
lr.JoinArticleName = articleName1;
// Child article.
lr.ArticleName = articleName2;
lr.FilterName = lrName;
lr.JoinUniqueKey = true;
lr.FilterTypes = FilterTypes.LogicalRecordLink;
lr.JoinFilterClause = lrClause;
// Add the logical record definition to the parent article.
article1.AddMergeJoinFilter(lr);
}
catch (Exception ex)
{
// Do error handling here and rollback the transaction.
throw new ApplicationException(
"The filtered articles could not be created", ex);
}
finally
{
conn.Disconnect();
}
' Define the Publisher and publication names.
Dim publisherName As String = publisherInstance
Dim publicationName As String = "AdvWorksSalesOrdersMerge"
Dim publicationDbName As String = "AdventureWorks2022"
' Specify article names.
Dim articleName1 As String = "SalesOrderHeader"
Dim articleName2 As String = "SalesOrderDetail"
' Specify logical record information.
Dim lrName As String = "SalesOrderHeader_SalesOrderDetail"
Dim lrClause As String = "[SalesOrderHeader].[SalesOrderID] = " _
& "[SalesOrderDetail].[SalesOrderID]"
Dim schema As String = "Sales"
Dim article1 As MergeArticle = New MergeArticle()
Dim article2 As MergeArticle = New MergeArticle()
Dim lr As MergeJoinFilter = New MergeJoinFilter()
Dim publication As MergePublication = New MergePublication()
' Create a connection to the Publisher.
Dim conn As ServerConnection = New ServerConnection(publisherName)
Try
' Connect to the Publisher.
conn.Connect()
' Verify that the publication uses precomputed partitions.
publication.Name = publicationName
publication.DatabaseName = publicationDbName
publication.ConnectionContext = conn
' If we can't get the properties for this merge publication, then throw an application exception.
If publication.LoadProperties() Then
' If precomputed partitions is disabled, enable it.
If publication.PartitionGroupsOption = PartitionGroupsOption.False Then
publication.PartitionGroupsOption = PartitionGroupsOption.True
End If
Else
Throw New ApplicationException(String.Format( _
"Settings could not be retrieved for the publication. " _
& "Ensure that the publication {0} exists on {1}.", _
publicationName, publisherName))
End If
' Set the required properties for the SalesOrderHeader article.
article1.ConnectionContext = conn
article1.Name = articleName1
article1.DatabaseName = publicationDbName
article1.SourceObjectName = articleName1
article1.SourceObjectOwner = schema
article1.PublicationName = publicationName
article1.Type = ArticleOptions.TableBased
' Set the required properties for the SalesOrderDetail article.
article2.ConnectionContext = conn
article2.Name = articleName2
article2.DatabaseName = publicationDbName
article2.SourceObjectName = articleName2
article2.SourceObjectOwner = schema
article2.PublicationName = publicationName
article2.Type = ArticleOptions.TableBased
If Not article1.IsExistingObject Then
article1.Create()
End If
If Not article2.IsExistingObject Then
article2.Create()
End If
' Define a logical record relationship between
' SalesOrderHeader and SalesOrderDetail.
' Parent article.
lr.JoinArticleName = articleName1
' Child article.
lr.ArticleName = articleName2
lr.FilterName = lrName
lr.JoinUniqueKey = True
lr.FilterTypes = FilterTypes.LogicalRecordLink
lr.JoinFilterClause = lrClause
' Add the logical record definition to the parent article.
article1.AddMergeJoinFilter(lr)
Catch ex As Exception
' Do error handling here and rollback the transaction.
Throw New ApplicationException( _
"The filtered articles could not be created", ex)
Finally
conn.Disconnect()
End Try