병합 테이블 아티클 간의 논리적 레코드 관계 정의
적용 대상: SQL Server
이 항목에서는 SQL Server Management Studio, Transact-SQL 또는 RMO(복제 관리 개체)를 사용하여 SQL Server의 병합 테이블 아티클 간에 논리적 레코드 관계를 정의하는 방법에 대해 설명합니다.
병합 복제를 사용하면 서로 다른 테이블의 관련 행 간에 관계를 정의할 수 있습니다. 그러면 동기화 중에 이러한 행을 하나의 트랜잭션 단위로 처리할 수 있습니다. 조인 필터 관계가 있는지 여부에 관계없이 두 아티클 간에 논리적 레코드를 정의할 수 있습니다. 자세한 내용은 논리적 레코드를 사용하여 관련된 행의 변경 내용 그룹화를 참조하세요.
참고 항목
SQL Server의 이후 버전에서는 이 기능이 제거됩니다. 새 개발 작업에서는 이 기능을 사용하지 않도록 하고, 현재 이 기능을 사용하는 애플리케이션은 수정하세요.
항목 내용
시작하기 전 주의 사항:
병합 테이블 아티클 간의 논리적 레코드 관계를 정의하려면 다음을 사용합니다:
시작하기 전에
제한 사항
- 게시에 대한 구독이 초기화된 후 논리 레코드를 추가, 수정 또는 삭제한 경우에는 변경 내용을 적용한 후에 새 스냅샷을 생성하고 모든 구독을 다시 초기화해야 합니다. 속성 변경 요구 사항에 대한 자세한 내용은 게시 및 아티클 속성 변경을 참조 하세요.
SQL Server Management Studio 사용
새 게시 마법사 및 게시 속성 - <게시> 대화 상자에서 사용할 수 있는 조인 추가 대화 상자에서 논리적 레코드를 정의합니다. 마법사 사용 및 대화 상자 액세스에 대한 자세한 내용은 게시 만들기 및 게시 속성 보기 및 수정을 참조하세요.
논리 레코드는 병합 게시의 조인 필터에 적용된 경우에만 조인 추가 대화 상자에서 정의할 수 있으며 게시는 사전 계산 파티션을 사용하기 위한 요구 사항을 따릅니다. 조인 필터에 적용되지 않는 논리 레코드를 정의하고 논리적 레코드 수준에서 충돌 검색 및 해결을 설정하려면 저장 프로시저를 사용해야 합니다.
논리적 레코드 관계를 정의하려면
새 게시 마법사의 테이블 행 필터 페이지 또는 게시 속성 - <게시>의 행 필터 페이지에 있는 필터링된 테이블 창에서 행 필터를 선택합니다.
논리적 레코드 관계는 조인 필터와 연결된 행 필터를 확장합니다. 따라서 조인 필터로 확장하기 전에 행 필터를 정의한 다음 논리적 레코드 관계를 적용해야 합니다. 하나의 조인 필터가 정의되면 다른 조인 필터를 사용하여 이 조인 필터를 확장할 수 있습니다. 조인 필터 정의 방법은 병합 아티클 사이에서 조인 필터 정의 및 수정을 참조하세요.
추가를 클릭한 다음 선택한 필터 확장을 위해 조인 추가를 클릭합니다.
조인 추가 대화 상자에서 조인 필터를 정의한 다음 논리 레코드 확인란을 선택합니다.
게시 속성 - <게시> 대화 상자에서 확인을 클릭하여 저장하고 대화 상자를 닫습니다.
논리적 레코드 관계를 삭제하려면
논리적 레코드 관계만 삭제하거나 논리 레코드 관계와 연결된 조인 필터를 삭제합니다.
논리적 레코드 관계만 삭제하려면:
새 게시 마법사의 행 필터 페이지 또는 게시 속성 - <게시> 대화 상자의 행 필터 페이지에서 필터링된 테이블 창에서 논리적 레코드 관계와 연결된 조인 필터를 선택한 다음 편집을 클릭합니다.
조인 편집 대화 상자에서 논리 레코드 확인란의 선택을 취소합니다.
확인을 선택합니다.
논리 레코드 관계를 삭제하고 연결된 조인 필터를 삭제하려면:
- 새 게시 마법사 또는 게시 속성 - <게시> 대화 상자의 행 필터 페이지에서 필터링된 테이블 창에서 필터를 선택한 다음 삭제를 클릭합니다. 삭제한 조인 필터 자체가 다른 조인에 의해 확장된 경우 해당 조인도 삭제됩니다.
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;
결과 집합의 필터 이름 열에서 제거되는 논리적 레코드 관계의 이름을 기록합니다.
참고 항목
이 쿼리는 sp_helpmergefilter와 동일한 정보를 반환하지만, 이 시스템 저장 프로시저는 조인 필터이기도 한 논리적 레코드 관계에 대한 정보만 반환합니다.
게시 데이터베이스의 게시자에서 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
RMO(복제 관리 개체) 사용
참고 항목
병합 복제를 사용하면 논리 레코드 수준에서 충돌을 추적하고 해결하도록 지정할 수 있지만 이러한 옵션은 RMO를 사용하여 설정할 수 없습니다.
연결된 조인 필터 없이 논리적 레코드 관계를 정의하려면
ServerConnection 클래스를 사용하여 게시자 연결을 만듭니다.
MergePublication 클래스의 인스턴스를 만들고 게시에 대한 Name 및 DatabaseName 속성을 설정한 다음, 1단계에서 만든 연결에 ConnectionContext 속성을 설정합니다.
LoadProperties 메서드를 호출하여 개체 속성을 가져옵니다. 이 메서드가 false를 반환하는 경우 2단계에서 게시 속성이 올바르게 정의되지 않았거나 게시가 없습니다.
PartitionGroupsOption 속성이 False로 설정되어 있으면 True로 설정합니다.
논리적 레코드를 구성하는 아티클이 없으면 MergeArticle 클래스의 인스턴스를 만들고 다음 속성을 설정합니다:
Name에 대한 아티클의 이름입니다.
PublicationName에 대한 게시의 이름입니다.
(선택 사항) 아티클이 가로로 필터링되는 경우 FilterClause 속성에 대한 행 필터 절을 지정합니다. 정적 또는 매개 변수가 있는 행 필터를 지정하려면 이 속성을 사용합니다. 자세한 내용은 매개 변수가 있는 행 필터를 참조하십시오.
자세한 내용은 아티클 정의를 참조하세요.
Create 메서드를 호출합니다.
논리 레코드를 구성하는 각 아티클에 대해 5단계와 6단계를 반복합니다.
아티클 간의 논리적 레코드 관계를 정의하는 MergeJoinFilter 클래스의 인스턴스를 만듭니다. 그리고 다음 속성을 설정합니다:
ArticleName 속성의 논리적 레코드 관계에 있는 자식 아티클의 이름입니다.
JoinArticleName 속성의 논리적 레코드 관계에 있는 기존 부모 아티클의 이름입니다.
FilterName 속성에 대한 논리적 레코드 관계의 이름입니다.
JoinFilterClause 속성의 관계를 정의하는 식입니다.
LogicalRecordLink 속성에 대한 FilterTypes 값입니다. 논리적 레코드 관계가 조인 필터이기도 한 경우 이 속성에 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