次の方法で共有


マージ テーブル アーティクル間に論理レコード リレーションシップを定義する

適用対象: SQL Server

このトピックでは、SQL Server で SQL Server Management Studio、Transact-SQL、またはレプリケーション管理オブジェクト (RMO) を使用して、マージ テーブル アーティクル間に論理レコード リレーションシップを定義する方法について説明します。

マージ レプリケーションでは、さまざまなテーブルの関連する行の間にリレーションシップを定義できます。 これらの行は、同期の際にトランザクション単位として処理できます。 論理レコードは、2 つのアーティクルの間に定義できます。結合フィルター リレーションシップの有無は関係ありません。 詳細については、「Group Changes to Related Rows with Logical Records」 (論理レコードによる関連行への変更のグループ化) を参照してください。

Note

この機能は、 SQL Serverの将来のバージョンで削除される予定です。 新規の開発作業ではこの機能を使用しないようにし、現在この機能を使用しているアプリケーションは修正することを検討してください。

このトピックの内容

始める前に

制限事項と制約事項

  • パブリケーションに対するサブスクリプションが初期化された後に、論理レコードを追加、変更、または削除した場合は、変更を行った後で、新しいスナップショットを生成し、すべてのサブスクリプションを再初期化する必要があります。 プロパティ変更の要件の詳細については、「Change Publication and Article Properties」(パブリケーションとアーティクルのプロパティの変更) をご覧ください。

SQL Server Management Studio を使用する

[結合の追加] ダイアログ ドロップダウン リストで論理レコードを定義します。このダイアログ ドロップダウン リストは、パブリケーションの新規作成ウィザードと [パブリケーションのプロパティ - <パブリケーション]> ダイアログ ドロップダウン リストで使用できます。 ウィザードの使用およびダイアログ ボックスへのアクセスの詳細については、「パブリケーションの作成」および「View and Modify Publication Properties」 (パブリケーション プロパティの表示および変更) を参照してください。

[結合の追加] ダイアログ ボックスで論理レコードを定義できるのは、マージ パブリケーションの結合フィルターに論理レコードが適用されている場合だけです。また、パブリケーションが、事前計算済みパーティションを使用するための要件を満たしている必要もあります。 結合フィルターに適用されていない論理レコードを定義して、論理レコード レベルでの競合の検出と解決を設定するには、ストアド プロシージャを使用する必要があります。

論理レコード リレーションシップを定義するには

  1. パブリケーションの新規作成ウィザードの [テーブル行のフィルター選択] ページまたは [パブリケーションのプロパティ - <パブリケーション]> ダイアログ ドロップダウン リストの [行のフィルター選択] ページで、[フィルター選択されたテーブル] ペイン内の行フィルターを選択します。

    論理レコード リレーションシップは、行フィルターを拡張する結合フィルターに関連付けられます。 したがって、先に行フィルターが定義されていないと、行フィルターを結合で拡張して論理レコード リレーションシップを適用することはできません。 1 つの結合フィルターが定義されると、この結合フィルターを別の結合フィルターを使用して拡張できます。 結合フィルターの定義の詳細については、「 マージ アーティクル間の結合フィルターの定義および変更」を参照してください。

  2. [追加]をクリックし、 [選択したフィルターを拡張するために結合を追加する]をクリックします。

  3. [結合の追加] ダイアログ ボックスで結合フィルターを定義してから、 [論理レコード]チェック ボックスをオンにします。

  4. [パブリケーションのプロパティ - <パブリケーション]> ダイアログ ボックスが表示されている場合は、[OK] をクリックして保存し、ダイアログ ボックスを閉じます。

論理レコード リレーションシップを削除するには

  • 論理レコード リレーションシップのみを削除するか、または論理レコード リレーションシップとそれに関連付けられている結合フィルターを削除します。

    論理レコード リレーションシップのみを削除するには

    1. パブリケーションの新規作成ウィザードの [行のフィルター選択] ページ、または [パブリケーションのプロパティ - <パブリケーション]> ダイアログ ドロップダウン リストの [行のフィルター選択] ページで、[フィルター選択されたテーブル] ペイン内の論理レコード リレーションシップに関連付けられている結合フィルターを選択し、[編集] をクリックします。

    2. [結合の編集] ダイアログ ボックスで、 [論理レコード]チェック ボックスをオフにします。

    3. [OK] を選択します。

    論理レコード リレーションシップとそれに関連付けられている結合フィルターを削除するには

    • パブリケーションの新規作成ウィザードの [行のフィルター選択] ページまたは [パブリケーションのプロパティ - <パブリケーション]> ダイアログ ドロップダウン リストで、[フィルター選択されたテーブル] ペイン内のフィルターを選択し、[削除する] をクリックします。 削除する結合フィルター自体が他の結合によって拡張されている場合は、それらの結合も削除されます。

Transact-SQL の使用

プログラムでレプリケーション ストアド プロシージャを使用して、アーティクル間に論理レコード リレーションシップを指定できます。

関連する結合フィルターを使用せずに論理レコード リレーションシップを定義するには

  1. フィルター選択されたアーティクルがパブリケーションに含まれている場合、 sp_helpmergepublicationを実行して、結果セットの use_partition_groups の値を確認します。

    • 値が 1の場合、事前計算済みパーティションが既に使用されています。

    • 値が 0の場合、パブリッシャー側のパブリケーション データベースに対して sp_changemergepublication を実行します。 @property には use_partition_groups を指定し、 @value には trueを指定します。

      Note

      パブリケーションで事前計算済みパーティションがサポートされない場合、論理レコードは使用できません。 詳細については、事前計算済みパーティションによるパラメーター化されたフィルター パフォーマンスの最適化に関するページで、事前計算済みパーティションを使用するための要件をご覧ください。

    • この値が NULL の場合、パブリケーションの初期スナップショットを生成するためにスナップショット エージェントを実行する必要があります。

  2. 論理レコードを構成するアーティクルが存在しない場合は、パブリッシャー側のパブリケーション データベースに対して sp_addmergearticle を実行します。 論理レコードの競合の検出と解決に関するオプションを次の中から 1 つ指定します。

    • 論理レコードの関連する行で発生する競合を検出し、解決するには、 @logical_record_level_conflict_detection および @logical_record_level_conflict_resolutiontrueを指定します。

    • 標準の行レベルまたは列レベルの競合の検出と解決を使用するには、 @logical_record_level_conflict_detection および @logical_record_level_conflict_resolutionfalseを指定します。これは既定の設定です。

  3. 論理レコードを構成する各アーティクルに対して、手順 2. を実行します。 論理レコード内の各アーティクルに使用する競合の検出および解決のオプションは、すべて同じである必要があります。 詳しくは、「 論理レコードの競合の検出および解決」をご覧ください。

  4. パブリッシャー側のパブリケーション データベースに対して、 sp_addmergefilterを実行します。 @publicationを指定します。リレーションシップの 1 つのアーティクルの名前を @articleに、もう 1 つのアーティクルの名前を @join_articlenameに指定します。リレーションシップの名前を @filternameに、2 つのアーティクル間のリレーションシップを定義する句を @join_filterclauseに、結合の種類を @join_unique_key に指定します。次のいずれかの値を @filter_typeに指定します。

    • 2 - 論理リレーションシップを定義します。

    • 3 - 結合フィルターを使用した論理リレーションシップを定義します。

    Note

    結合フィルターを使用しない場合、2 つのアーティクル間のリレーションシップの方向はあまり重要でなくなります。

  5. パブリケーションの他の論理レコード リレーションシップについても、手順 2. をそれぞれ実行します。

論理レコードにおける競合の検出と解決の方法を変更するには

  1. 論理レコードの関連する行で発生する競合を検出し、解決するには、次の手順を実行します。

    • パブリッシャー側のパブリケーション データベースに対して、 sp_changemergearticleを実行します。 @propertylogical_record_level_conflict_detection を指定し、 @valuetrueを指定します。 @force_invalidate_snapshot および @force_reinit_subscription1を指定します。

    • パブリッシャー側のパブリケーション データベースに対して、 sp_changemergearticleを実行します。 @propertylogical_record_level_conflict_resolution を指定し、 @valuetrueを指定します。 @force_invalidate_snapshot および @force_reinit_subscription1を指定します。

  2. 標準の行レベルまたは列レベルの競合の検出と解決を使用するには、次の手順を実行します。

    • パブリッシャー側のパブリケーション データベースに対して、 sp_changemergearticleを実行します。 @propertylogical_record_level_conflict_detection を指定し、 @valuefalseを指定します。 @force_invalidate_snapshot および @force_reinit_subscription1を指定します。

    • パブリッシャー側のパブリケーション データベースに対して、 sp_changemergearticleを実行します。 @propertylogical_record_level_conflict_resolution を指定し、 @valuefalseを指定します。 @force_invalidate_snapshot および @force_reinit_subscription1を指定します。

論理レコード リレーションシップを削除するには

  1. パブリッシャー側のパブリケーション データベースに対して次のクエリを実行し、指定されたパブリケーションに対して定義されているすべての論理レコード リレーションシップに関する情報を返します。

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

    結果セット内の filtername 列で削除されている論理レコード リレーションシップの名前を確認します。

    Note

    このクエリは、 sp_helpmergefilterと同じ情報を返します。ただし、このシステム ストアド プロシージャは、結合フィルターでもある論理レコード リレーションシップに関する情報のみ返します。

  2. パブリッシャー側のパブリケーション データベースに対して、 sp_dropmergefilterを実行します。 @publicationを指定し、リレーションシップ内の 1 つのアーティクルの名前を @articleに、手順 1. のリレーションシップの名前を @filternameに指定します。

例 (Transact-SQL)

この例では、既存のパブリケーションで事前計算済みパーティションを有効にし、 SalesOrderHeader テーブルと SalesOrderDetail テーブルの 2 つの新しいアーティクルを含む論理レコードを作成しています。

-- 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) の使用

Note

マージ レプリケーションを使用して、論理レコード レベルで追跡および解決する競合を指定できますが、これらのオプションは RMO を使用して設定できません。

関連する結合フィルターを使用せずに論理レコード リレーションシップを定義するには

  1. ServerConnection クラスを使用して、パブリッシャーへの接続を作成します。

  2. MergePublication クラスのインスタンスを作成し、パブリケーションの Name プロパティと DatabaseName プロパティを設定して、手順 1. で作成した接続を ConnectionContext プロパティに設定します。

  3. LoadProperties メソッドを呼び出して、オブジェクトのプロパティを取得します。 このメソッドが falseを返す場合、手順 2. でパブリケーション プロパティを不適切に設定したか、パブリケーションが存在していません。

  4. PartitionGroupsOption プロパティが Falseに設定されている場合、これを Trueに設定します。

  5. 論理レコードを構成するアーティクルが存在しない場合は、 MergeArticle クラスのインスタンスを作成し、次のプロパティを設定します。

    • Nameにアーティクル名を指定します。

    • PublicationName にパブリケーション名を指定します。

    • (省略可) アーティクルが行方向にフィルター選択される場合、 FilterClause プロパティに行フィルター句を指定します。 このプロパティを使用して、静的行フィルターまたはパラメーター化された行フィルターを指定します。 詳しくは、「 Parameterized Row Filters」をご覧ください。

    詳しくは、「 アーティクルを定義」をご覧ください。

  6. Create メソッドを呼び出します。

  7. 論理レコードを構成するアーティクルごとに、手順 5. と 6. を繰り返します。

  8. MergeJoinFilter クラスのインスタンスを作成して、アーティクル間の論理レコード リレーションシップを定義します。 その後、次のプロパティを設定します。

    • ArticleName プロパティに論理レコード リレーションシップの子アーティクルの名前を指定します。

    • JoinArticleName プロパティに論理レコード リレーションシップの既存の親アーティクルの名前を指定します。

    • FilterName プロパティに論理レコード リレーションシップの名前を指定します。

    • リレーションシップを定義する式を JoinFilterClause プロパティに指定します。

    • LogicalRecordLink の値を FilterTypes プロパティに指定します。 論理レコード リレーションシップが結合フィルターでもある場合は、このプロパティに JoinFilterAndLogicalRecordLink の値を指定します。 詳細については、「Group Changes to Related Rows with Logical Records」 (論理レコードによる関連行への変更のグループ化) を参照してください。

  9. リレーションシップ内の子アーティクルを表すオブジェクトに対して AddMergeJoinFilter メソッドを呼び出します。 手順 8. の MergeJoinFilter オブジェクトを渡して、リレーションシップを定義します。

  10. パブリケーションの他の論理レコード リレーションシップについても、手順 8. と 9. をそれぞれ実行します。

例 (RMO)

この例では、 SalesOrderHeader テーブルと SalesOrderDetail テーブルの 2 つの新しいアーティクルを含む論理レコードを作成しています。

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