병합 테이블 아티클 간의 논리적 레코드 관계 정의

이 항목에서는 SQL Server Management Studio, Transact-SQL 또는 RMO(복제 관리 개체)를 사용하여 SQL Server 2012에서 테이블 아티클 간 논리적 레코드 관계를 정의하는 방법에 대해 설명합니다.

병합 복제를 사용하면 서로 다른 테이블에 있는 관련 행 간의 관계를 정의할 수 있습니다. 그러면 동기화 중에 이러한 행을 하나의 트랜잭션 단위로 처리할 수 있습니다. 논리적 레코드는 조인 필터 관계가 있는지 여부와 관계없이 두 아티클 간에 정의할 수 있습니다. 자세한 내용은 논리적 레코드를 사용하여 관련된 행의 변경 내용 그룹화를 참조하십시오.

[!참고]

Microsoft SQL Server의 이후 버전에서는 이 기능이 제거됩니다. 새 개발 작업에서는 이 기능을 사용하지 않도록 하고, 현재 이 기능을 사용하는 응용 프로그램은 수정하십시오.

항목 내용

  • 시작하기 전에:

    제한 사항

  • 다음을 사용하여 병합 테이블 아티클 간의 논리적 레코드 관계를 정의하려면

    SQL Server Management Studio

    Transact-SQL

    RMO(복제 관리 개체)

시작하기 전에

제한 사항

  • 게시에 대한 구독이 초기화된 후 논리적 레코드를 추가, 수정 또는 삭제한 경우에는 변경 내용을 적용한 후에 새 스냅숏을 생성하고 모든 구독을 다시 초기화해야 합니다. 속성 변경 요구 사항에 대한 자세한 내용은 게시 및 아티클 속성 변경을 참조하십시오.

맨 위로 이동 링크와 함께 사용되는 화살표 아이콘[Top]

SQL Server Management Studio 사용

새 게시 마법사 및 게시 속성 - <Publication> 대화 상자의 조인 추가 대화 상자에서 논리적 레코드를 정의할 수 있습니다. 마법사를 사용하는 방법 및 대화 상자에 액세스하는 방법은 게시 만들기게시 속성 보기 및 수정을 참조하십시오.

논리적 레코드가 병합 게시의 조인 필터에 적용되고 게시가 사전 계산 파티션을 사용하기 위한 요구 사항을 따르는 경우에만 조인 추가 대화 상자에서 논리적 레코드를 정의할 수 있습니다. 조인 필터에 적용되지 않는 논리적 레코드를 정의하고 논리적 레코드 수준에서 충돌 감지 및 해결을 설정하려면 저장 프로시저를 사용해야 합니다.

논리적 레코드 관계를 정의하려면

  1. 새 게시 마법사의 테이블 행 필터 페이지 또는 게시 속성 - <Publication> 대화 상자의 행 필터 페이지에서 필터링된 테이블 창의 행 필터를 선택합니다.

    논리적 레코드 관계는 조인 필터와 연결된 행 필터를 확장합니다. 따라서 조인 필터로 확장하기 전에 행 필터를 정의한 다음 논리적 레코드 관계를 적용해야 합니다. 한 조인 필터를 정의한 후에 다른 조인 필터를 사용하여 이 조인 필터를 확장할 수 있습니다. 조인 필터 정의 방법은 병합 아티클 사이에서 조인 필터 정의 및 수정을 참조하십시오.

  2. 추가를 클릭한 다음 선택한 필터 확장을 위해 조인 추가를 클릭합니다.

  3. 조인 추가 대화 상자에서 조인 필터를 정의한 다음 논리적 레코드 확인란을 선택합니다.

  4. 게시 속성 - <Publication> 대화 상자에서 확인을 클릭하여 저장하고 대화 상자를 닫습니다.

논리적 레코드 관계를 삭제하려면

  • 논리적 레코드 관계만 삭제하거나 논리적 레코드 관계 및 이와 관련된 조인 필터를 함께 삭제합니다.

    논리적 레코드 관계만 삭제하려면

    1. 새 게시 마법사의 행 필터 페이지 또는 게시 속성 - <Publication> 대화 상자의 행 필터 페이지에서 필터링된 테이블 창의 논리적 레코드 관계와 관련된 조인 필터를 선택한 다음 편집을 클릭합니다.

    2. 조인 편집 대화 상자에서 논리적 레코드 확인란 선택을 취소합니다.

    3. 확인을 클릭합니다.

    논리적 레코드 관계 및 이와 관련된 조인 필터를 함께 삭제하려면

    • 새 게시 마법사의 행 필터 페이지 또는 게시 속성 - <Publication> 대화 상자에서 필터링된 테이블 창의 테이블을 선택한 다음 삭제를 클릭합니다. 삭제하는 조인 필터가 다른 조인에 의해 확장된 경우 해당 조인 또한 삭제됩니다.

맨 위로 이동 링크와 함께 사용되는 화살표 아이콘[Top]

Transact-SQL 사용

복제 저장 프로시저를 사용하여 아티클 간 논리적 레코드 관계를 프로그래밍 방식으로 지정할 수 있습니다.

관련된 조인 필터 없이 논리적 레코드 관계를 정의하려면

  1. 게시에 필터링된 아티클이 포함되어 있으면 sp_helpmergepublication을 실행하고 결과 집합에서 use_partition_groups의 값을 확인합니다.

    • 이 값이 1이면 사전 계산 파티션이 이미 사용되고 있는 것입니다.

    • 이 값이 0이면 게시 데이터베이스의 게시자에서 sp_changemergepublication을 실행합니다. @propertyuse_partition_groups 값을 지정하고 @valuetrue 값을 지정합니다.

      [!참고]

      게시에서 사전 계산 파티션을 지원하지 않으면 논리적 레코드를 사용할 수 없습니다. 자세한 내용은 사전 계산 파티션으로 매개 변수가 있는 필터 성능 최적화 항목의 사전 계산 파티션을 사용하기 위한 요구 사항을 참조하십시오.

    • 이 값이 NULL이면 스냅숏 에이전트를 실행하여 게시에 대한 초기 스냅숏을 생성해야 합니다.

  2. 논리적 레코드를 구성하는 아티클이 없으면 게시 데이터베이스의 게시자에서 sp_addmergearticle을 실행합니다. 논리적 레코드에 대해 다음 충돌 감지 및 해결 옵션 중 하나를 지정합니다.

    • 논리적 레코드의 관련 행 내에서 발생하는 충돌을 감지하여 해결하려면 @logical_record_level_conflict_detection@logical_record_level_conflict_resolution 값을 true로 지정합니다.

    • 표준 행 수준 또는 열 수준의 충돌 감지 및 해결을 사용하려면 @logical_record_level_conflict_detection@logical_record_level_conflict_resolutionfalse 값(기본값)을 지정합니다.

  3. 논리적 레코드를 구성하는 각 아티클에 대해 2단계를 반복합니다. 논리적 레코드의 각 아티클에 대해 동일한 충돌 감지 및 해결 옵션을 사용해야 합니다. 자세한 내용은 논리적 레코드에서 충돌 감지 및 해결을 참조하십시오.

  4. 게시 데이터베이스의 게시자에서 sp_addmergefilter를 실행합니다. @publication을 지정하고 @article에 관계 구성 아티클 중 하나의 이름을, @join_articlename에 두 번째 아티클의 이름을, @filtername에 관계의 이름을, @join_filterclause에 두 아티클 간 관계를 정의하는 절을, @join_unique_key에 조인 형식을 지정하고 @filter_type에 다음 값 중 하나를 지정합니다.

    • 2 - 논리적 관계를 정의합니다.

    • 3 - 조인 필터를 포함하는 논리적 관계를 정의합니다.

    [!참고]

    조인 필터를 사용하지 않는 경우 두 아티클 간의 관계 방향은 중요하지 않습니다.

  5. 게시에서 남은 각각의 논리적 레코드 관계에 대해 2단계를 반복합니다.

논리적 레코드에 대한 충돌 감지 및 해결을 변경하려면

  1. 논리적 레코드의 관련 행 내에서 발생하는 충돌을 감지하고 해결하려면 다음을 수행합니다.

    • 게시 데이터베이스의 게시자에서 sp_changemergearticle을 실행합니다. @propertylogical_record_level_conflict_detection 값을 지정하고 @valuetrue 값을 지정합니다. @force_invalidate_snapshot@force_reinit_subscription에 값 1을 지정합니다.

    • 게시 데이터베이스의 게시자에서 sp_changemergearticle을 실행합니다. @propertylogical_record_level_conflict_resolution 값을 지정하고 @valuetrue 값을 지정합니다. @force_invalidate_snapshot@force_reinit_subscription에 값 1을 지정합니다.

  2. 표준 행 수준 또는 열 수준의 충돌 감지 및 해결을 사용하려면 다음을 수행합니다.

    • 게시 데이터베이스의 게시자에서 sp_changemergearticle을 실행합니다. @propertylogical_record_level_conflict_detection 값을 지정하고 @valuefalse 값을 지정합니다. @force_invalidate_snapshot@force_reinit_subscription에 값 1을 지정합니다.

    • 게시 데이터베이스의 게시자에서 sp_changemergearticle을 실행합니다. @propertylogical_record_level_conflict_resolution 값을 지정하고 @valuefalse 값을 지정합니다. @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에 대해 관계 구성 아티클 중 하나의 이름을, @filtername에 대해 1단계에서 사용된 관계의 이름을 지정합니다.

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

맨 위로 이동 링크와 함께 사용되는 화살표 아이콘[Top]

RMO(복제 관리 개체) 사용

[!참고]

병합 복제를 사용하면 충돌을 추적하고 논리적 레코드 수준에서 충돌을 해결하도록 지정할 수도 있지만 RMO를 사용하는 경우에는 이러한 옵션을 설정할 수 없습니다.

관련된 조인 필터 없이 논리적 레코드 관계를 정의하려면

  1. ServerConnection 클래스를 사용하여 게시자 연결을 만듭니다.

  2. MergePublication 클래스의 인스턴스를 만들고 게시에 대한 NameDatabaseName 속성을 설정한 다음, 1단계에서 만든 연결에 ConnectionContext 속성을 설정합니다.

  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)

이 예제에서는 SalesOrderHeader 및 SalesOrderDetail 테이블의 새 아티클 두 개를 구성하는 논리적 레코드를 만듭니다.

// Define the Publisher and publication names.
string publisherName = publisherInstance;
string publicationName = "AdvWorksSalesOrdersMerge";
string publicationDbName = "AdventureWorks2012";

// 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 = "AdventureWorks2012"

' 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

맨 위로 이동 링크와 함께 사용되는 화살표 아이콘[Top]

참고 항목

개념

병합 아티클 사이에서 조인 필터 정의 및 수정

병합 아티클에 대한 매개 변수가 있는 행 필터 정의 및 수정

정적 행 필터 정의 및 수정

논리적 레코드를 사용하여 관련된 행의 변경 내용 그룹화

병합 테이블 아티클 간의 논리적 레코드 관계 정의

사전 계산 파티션으로 매개 변수가 있는 필터 성능 최적화

논리적 레코드를 사용하여 관련된 행의 변경 내용 그룹화