共用方式為


定義合併資料表發行項之間的邏輯記錄關聯性

適用於:SQL Server

本主題說明如何使用 SQL Server Management Studio、Transact-SQL 或 Replication Management Objects (RMO),在 SQL Server 中定義合併資料表發行項之間的邏輯記錄關聯性。

合併式複寫可讓您在不同資料表內定義相關資料列之間的關聯性。 然後這些資料列在同步處理期間,可以當做交易式單位來處理。 可以在兩個發行項之間定義邏輯記錄,不論這些發行項是否有聯結篩選關聯性。 如需詳細資訊,請參閱使用邏輯記錄分組相關資料列的變更

注意

SQL Server 的未來版本將移除此功能。 請避免在新的開發工作中使用這項功能,並規劃修改目前使用這項功能的應用程式。

本主題內容

開始之前

限制事項

  • 如果您在初始化發行集的訂閱後,新增、修改或刪除邏輯記錄,則必須在進行變更後產生新的快照集並重新初始化所有訂閱。 如需屬性變更需求的詳細資訊,請參閱變更發行集與發行項屬性

使用 SQL Server Management Studio

您可以在位於 [新增發行集精靈] 和 [發行集屬性 - <發行集>] 對話方塊的 [新增聯結] 對話方塊中,定義邏輯記錄。 如需使用精靈及存取對話方塊的詳細資訊,請參閱建立發行集檢視及修改發行集屬性

只有將邏輯記錄套用至合併式發行集中的聯結篩選,並且該發行集符合使用預先計算的資料分割要求時,方可在 [加入聯結] 對話方塊中定義這些邏輯記錄。 若要定義未套用至聯結篩選的邏輯記錄,並在邏輯記錄層級設定衝突偵測和解決方案,您必須使用預存程序。

若要定義邏輯記錄關聯性

  1. 在 [新增發行集精靈] 的 [篩選資料表的資料列] 頁面上,或是在 [發行集屬性 - <發行集>] 對話方塊的 [篩選資料列] 頁面上,從 [已篩選的資料表] 窗格中選取一個資料列篩選。

    邏輯記錄關聯性與聯結篩選相關聯,這會擴充資料列篩選。 因此,您必須在使用聯結擴充篩選並套用邏輯記錄關聯性之前,先定義資料列篩選。 定義好一個聯結篩選後,您可以以另一個聯結篩選擴充這個聯結篩選。 如需定義聯結篩選的詳細資訊,請參閱< 定義和修改合併發行項之間的聯結篩選>。

  2. 按一下 [加入] ,然後按一下 [加入聯結以擴充選取的篩選]

  3. [加入聯結] 對話方塊中定義聯結篩選,然後選取 [邏輯記錄] 核取方塊。

  4. 如果您在 [發行集屬性 - <發行集>] 對話方塊中,請按一下 [確定] 以儲存並關閉對話方塊。

若要刪除邏輯記錄關聯性

  • 僅刪除邏輯記錄關聯性,或刪除邏輯記錄關聯性及與其相關聯的聯結篩選。

    若要僅刪除邏輯記錄關聯性:

    1. 在 [新增發行集精靈] 的 [篩選資料列] 頁面上,或是在 [發行集屬性 - <發行集>] 對話方塊的 [篩選資料列] 頁面上,從 [已篩選的資料表] 窗格中選取與邏輯記錄關聯性相關聯的聯結篩選,然後按一下 [編輯]。

    2. [編輯聯結] 對話方塊中,清除 [邏輯記錄] 核取方塊。

    3. 選取 [確定]。

    若要刪除邏輯記錄關聯性及與其關聯的聯結篩選:

    • 在 [新增發行集精靈] 或 [發行集屬性 - <發行集>] 對話方塊的 [篩選資料列] 頁面上,從 [已篩選的資料表] 窗格中選取一個篩選,然後按一下 [刪除]。 如果您刪除的聯結篩選本身已由其他聯結擴充,也會一併刪除這些聯結。

使用 TRANSACT-SQL

您可以使用複寫預存程序,以程式設計方式指定發行項之間的邏輯記錄關聯性。

定義沒有相關聯結篩選的邏輯記錄關聯性

  1. 如果發行集包含任何篩選的發行項,請執行 sp_helpmergepublication,並記下結果集中的 use_partition_groups 值。

    • 如果這個值是 1,則表示已經使用預先計算的資料分割。

    • 如果這個值是 0,請在發行集資料庫的發行者上執行 sp_changemergepublication 。 請為 @property 指定 use_partition_groups 的值,並為 @value 指定 true的值。

      注意

      如果發行集不支援預先計算的資料分割,將無法使用邏輯記錄。 如需詳細資訊,請參閱使用預先計算的資料分割最佳化參數化篩選效能主題中的<使用預先計算的資料分割之需求>。

    • 如果此值為 NULL,則必須執行快照集代理程式,才能為發行集產生初始快照集。

  2. 如果組成此邏輯記錄的發行項不存在,請在發行集資料庫的發行者上執行 sp_addmergearticle 。 針對此邏輯記錄指定下列其中一個衝突偵測和解決選項:

    • 若要偵測及解決邏輯記錄中相關資料列內所發生的衝突,請為 @logical_record_level_conflict_detection@logical_record_level_conflict_resolution 指定 true的值。

    • 若要使用標準資料列層級或資料行層級的衝突偵測與解決方法,請為 @logical_record_level_conflict_detection@logical_record_level_conflict_resolution 指定 false的值 (這是預設值)。

  3. 針對組成此邏輯記錄的每一個發行項重複步驟 2。 您必須針對此邏輯記錄中的每一個發行項使用相同的衝突偵測和解決選項。 如需詳細資訊,請參閱 偵測和解決邏輯記錄中的衝突

  4. 在發行集資料庫的發行者上,執行 sp_addmergefilter。 指定 @publication、關聯性中 @article的一個發行項名稱、 @join_articlename的第二個發行項名稱、 @filtername的關聯性名稱、為 @join_filterclause定義兩個發行項之間之關聯性的子句、 @join_unique_key 的聯結類型,以及 @filter_type的下列其中一個值:

    • 2 - 定義邏輯關聯性。

    • 3 - 定義具有聯結篩選的邏輯關聯性。

    注意

    如果未使用聯結篩選,則兩個發行項之間的關聯性方向就不是那麼重要。

  5. 針對發行集中每一個剩餘的邏輯記錄關聯性重複步驟 2。

變更邏輯記錄的衝突偵測和解決方式

  1. 若要偵測及解決邏輯記錄中相關資料列內所發生的衝突:

    • 在發行集資料庫的發行者上,執行 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的值。

  2. 若要使用標準資料列層級或資料行層級的衝突偵測與解決方式:

    • 在發行集資料庫的發行者上,執行 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的值。

移除邏輯記錄關聯性

  1. 在發行集資料庫的發行者上,執行下列查詢來傳回有關針對指定之發行集定義之所有邏輯記錄關聯性的資訊:

    SELECT f.* FROM sysmergesubsetfilters AS f 
    INNER JOIN sysmergepublications AS p
    ON f.pubid = p.pubid WHERE p.[name] = @publication;
    

    請注意從結果集中 filtername 資料行所移除的邏輯記錄關聯性名稱。

    注意

    此查詢會傳回與 sp_helpmergefilter相同的資訊,但是此系統預存程序只會傳回也屬於聯結篩選之邏輯記錄關聯性的相關資訊。

  2. 在發行集資料庫的發行者上,執行 sp_dropmergefilter。 指定 @publication、關聯性中 @article的其中一個發行項名稱,以及步驟 1 中 @filtername的關聯性名稱。

範例 (Transact-SQL)

這個範例會在現有的發行集上啟用預先計算的資料分割,並針對 SalesOrderHeaderSalesOrderDetail 資料表建立組成兩個新發行項的邏輯記錄。

-- 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

使用 Replication Management Objects (RMO)

注意

合併式複寫可允許您指定在邏輯記錄層級追蹤及解決衝突,但這些選項無法使用 RMO 來設定。

定義沒有相關聯結篩選的邏輯記錄關聯性

  1. 使用 ServerConnection 類別建立與發行者的連接。

  2. 建立 MergePublication 類別的執行個體、為發行集設定 NameDatabaseName 屬性,以及將 ConnectionContext 屬性設定為步驟 1 中所建立的連接。

  3. 呼叫 LoadProperties 方法以取得物件的屬性。 如果此方法傳回 false,則表示步驟 2 中的發行集屬性定義不正確,或者該發行集不存在。

  4. 如果 PartitionGroupsOption 屬性設定為 False,請將它設定為 True

  5. 如果組成此邏輯記錄的發行項不存在,請建立 MergeArticle 類別的執行個體,並設定以下屬性:

    • Name設定為發行項名稱。

    • PublicationName設定為發行集名稱。

    • (選擇性) 如果以水平方式篩選此發行項,請為 FilterClause 屬性指定資料列篩選子句。 使用此屬性可指定靜態或參數化資料列篩選器。 如需詳細資訊,請參閱< 參數化資料列篩選器>。

    如需詳細資訊,請參閱 定義發行項

  6. 呼叫 Create 方法。

  7. 針對組成此邏輯記錄的每一個發行項重複執行步驟 5 和 6。

  8. 建立 MergeJoinFilter 類別的執行個體,以定義發行項之間的邏輯記錄關聯性。 然後,設定下列屬性:

  9. 在代表關聯性中子發行項的物件上,呼叫 AddMergeJoinFilter 方法。 傳遞步驟 8 中的 MergeJoinFilter 物件來定義關聯性。

  10. 針對發行集中每一個剩餘的邏輯記錄關聯性重複步驟 8 和 9。

範例 (RMO)

這個範例會針對 SalesOrderHeaderSalesOrderDetail 資料表建立組成兩個新發行項的邏輯記錄。

           // 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