Defina uma relação de registo lógico entre artigos de tabelas de fusão

Aplica-se a: SQL Server

Este tópico descreve como definir uma relação de registo lógico entre artigos de tabelas de fusão no SQL Server utilizando o SQL Server Management Studio, Transact-SQL ou Objetos de Gestão de Replicação (RMO).

A replicação por fusão permite-lhe definir uma relação entre linhas relacionadas em diferentes tabelas. Estas linhas podem então ser processadas como uma unidade transacional durante a sincronização. Um registo lógico pode ser definido entre dois artigos, independentemente de terem ou não uma relação de filtro de junção. Para obter mais informações, consulte Agrupar alterações em linhas relacionadas com registros lógicos.

Note

Esse recurso será removido em uma versão futura do SQL Server. Evite usar esse recurso em novos trabalhos de desenvolvimento e planeje modificar aplicativos que atualmente usam esse recurso.

Limitações e Restrições

  • Se adicionar, modificar ou eliminar um registo lógico após as subscrições da publicação terem sido inicializadas, deve gerar um novo snapshot e reinicializar todas as subscrições após efetuar a alteração. Para mais informações sobre os requisitos para alterações às propriedades, consulte Alterar Propriedades de Publicação e de Artigo.

Usando o SQL Server Management Studio

Defina registos lógicos na caixa de diálogo Adicionar associação, que está disponível no Assistente de Nova Publicação e na caixa de diálogo Propriedades da Publicação - <Publicação>. Para mais informações sobre como usar o assistente e aceder à caixa de diálogo, veja Criar uma Publicação e Visualizar e Modificar Propriedades de Publicação.

Os registos lógicos só podem ser definidos na caixa de diálogo Adicionar Junção apenas se forem aplicados a um filtro de associação numa publicação de intercalação e se a publicação cumprir os requisitos para utilizar partições pré-computadas. Para definir registos lógicos que não são aplicados a filtros de junção e para definir a deteção e resolução de conflitos ao nível do registo lógico, deve usar procedimentos armazenados.

Para definir uma relação de registo lógico

  1. Na página Filtrar Linhas da Tabela do Assistente para Nova Publicação ou na página Filtrar Linhas da caixa de diálogo Propriedades da Publicação - <Publicação>, selecione um filtro de linhas no painel Tabelas Filtradas.

    Uma relação de registo lógico está associada a um filtro de junção, que estende um filtro de linha. Por isso, deve definir um filtro de linhas antes de poder estender o filtro com uma junção e aplicar uma relação lógica entre registos. Depois de definir um filtro de junção, pode estender este filtro de junção com outro filtro de junção. Para mais informações sobre a definição de filtros de junção, veja Definir e Modificar um Filtro de Junção Entre Artigos de Fusão.

  2. Clica em Adicionar e depois em Adicionar Juntar para estender o filtro selecionado.

  3. Defina um filtro de junção na caixa de diálogo Adicionar Junção e depois selecione a caixa de seleção Registo Lógico.

  4. Se estiver na caixa de diálogo Propriedades de Publicação - <Publicação> , clique em OK para guardar e fechar a caixa de diálogo.

Para eliminar uma relação entre registos lógicos

  • Apague apenas a relação de registo lógico ou apague a relação de registo lógico e o filtro de junção associado a ela.

    Para eliminar apenas a relação de registo lógico:

    1. Na página Filtrar Linhas do Assistente de Nova Publicação ou na página Filtrar Linhas da caixa de diálogo Propriedades de Publicação - <Publicação>, selecione o filtro de junção associado à relação de registo lógico no painel Tabelas Filtradas e, em seguida, clique em Editar.

    2. Na caixa de diálogo Editar associação, desmarque a caixa de verificação Registo Lógico.

    3. Selecione OK.

    Para eliminar a relação de registo lógico e o filtro de junção associado a ela:

    • Na página de Linhas de Filtro do Assistente de Nova Publicação ou na caixa de diálogo Propriedades de Publicação - <Publicação> , selecione um filtro no painel de Tabelas Filtradas e depois clique em Eliminar. Se o filtro de junção que eliminas for ele próprio estendido por outras junções, essas junções também serão eliminadas.

Usando Transact-SQL

Pode especificar programaticamente as relações de registo lógico entre artigos utilizando procedimentos armazenados de replicação.

Definir uma relação de registo lógico sem um filtro de junção associado

  1. Se a publicação contiver artigos filtrados, execute sp_helpmergepublication e indique o valor de use_partition_groups no conjunto de resultados.

    • Se o valor for 1, então partições pré-computadas já estão a ser usadas.

    • Se o valor for 0, então execute sp_changemergepublication no Publisher, na base de dados da publicação. Especifique um valor de use_partition_groups para @property e um valor de true para @value.

      Note

      Se a publicação não suportar partições pré-computadas, então não podem ser usados registos lógicos. Para mais informações, consulte Requisitos para Utilização de Partições Pré-computadas no tópico Otimizar o Desempenho de Filtros Parametrizados com Partições Pré-computadas.

    • Se o valor for NULL, então é necessário executar o Snapshot Agent para gerar o snapshot inicial da publicação.

  2. Se os artigos que irão constituir o registo lógico não existirem, execute sp_addmergearticle no Publicador, na base de dados de publicação. Especifique uma das seguintes opções de deteção e resolução de conflitos para o registo lógico:

    • Para detetar e resolver conflitos que ocorram dentro de linhas relacionadas no registo lógico, especifique um valor de verdadeiro para @logical_record_level_conflict_detection e @logical_record_level_conflict_resolution.

    • Para usar a deteção e resolução padrão de conflitos ao nível de linha ou coluna, especifique um valor de false para @logical_record_level_conflict_detection e @logical_record_level_conflict_resolution, que é o padrão.

  3. Repita o passo 2 para cada artigo que compõe o registo lógico. Deve usar a mesma opção de deteção e resolução de conflitos para cada artigo no registo lógico. Para obter mais informações, consulte Detetando e resolvendo conflitos em registros lógicos.

  4. No publicador, na base de dados de publicação, execute sp_addmergefilter. Especifique @publication, o nome de um artigo na relação para @article, o nome do segundo artigo para @join_articlename, um nome para a relação para @filtername, uma cláusula que defina a relação entre os dois artigos para @join_filterclause, o tipo de junção para @join_unique_key e um dos seguintes valores para @filter_type:

    • 2 - Define uma relação lógica.

    • 3 - Define uma relação lógica com um filtro de junção.

    Note

    Se não for utilizado um filtro de junção, a direção da relação entre os dois artigos não é importante.

  5. Repita o passo 2 para cada relação de registo lógico restante na publicação.

Para alterar a deteção e a resolução de conflitos para registos lógicos

  1. Para detetar e resolver conflitos que ocorrem dentro de linhas relacionadas no registo lógico:

    • No Publisher na base de dados de publicação, execute sp_changemergearticle. Defina o valor logical_record_level_conflict_detection para @property e o valor true para @value. Especifique um valor de 1 para @force_invalidate_snapshot e @force_reinit_subscription.

    • No Publisher na base de dados de publicação, execute sp_changemergearticle. Especifique o valor logical_record_level_conflict_resolution para @property e o valor true para @value. Especifique um valor de 1 para @force_invalidate_snapshot e @force_reinit_subscription.

  2. Para usar a deteção e resolução padrão de conflitos ao nível de linha ou coluna:

    • No Publisher na base de dados de publicação, execute sp_changemergearticle. Especifique o valor logical_record_level_conflict_detection para @property e o valor false para @value. Especifique um valor de 1 para @force_invalidate_snapshot e @force_reinit_subscription.

    • No Publisher na base de dados de publicação, execute sp_changemergearticle. Especifique um valor de logical_record_level_conflict_resolution para @property e o valor false para @value. Especifique um valor de 1 para @force_invalidate_snapshot e @force_reinit_subscription.

Para eliminar uma relação entre registos lógicos

  1. No Publisher da base de dados de publicações, execute a seguinte consulta para devolver informações sobre todas as relações lógicas de registo definidas para a publicação especificada:

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

    Note que o nome da relação de registo lógico está a ser removido na coluna nome do filtro no conjunto de resultados.

    Note

    Esta consulta retorna a mesma informação que sp_helpmergefilter; No entanto, este procedimento armazenado do sistema apenas devolve informações sobre relações de registo lógico que também são filtros de junção.

  2. No Publisher da base de dados de publicações, execute sp_dropmergefilter. Especifique @publication, o nome de um dos artigos na relação para @article e o nome da relação do passo 1 para @filtername.

Exemplo (Transact-SQL)

Este exemplo ativa partições pré-computadas numa publicação existente e cria um registo lógico composto pelos dois novos artigos para as tabelas SalesOrderHeader e 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

Utilização de Objetos de Gestão de Replicação (RMO)

Note

A replicação por fusão permite especificar que os conflitos sejam rastreados e resolvidos ao nível do registo lógico, mas estas opções não podem ser definidas usando RMO.

Definir uma relação de registo lógico sem um filtro de junção associado

  1. Crie uma conexão com o Publisher usando a classe ServerConnection.

  2. Crie uma instância da MergePublication classe, defina as Name propriedades e DatabaseName para a publicação e defina a ConnectionContext propriedade para a ligação criada no passo 1.

  3. Chame o método LoadProperties para obter as propriedades do objeto. Se esse método retornar false, as propriedades de publicação na etapa 2 foram definidas incorretamente ou a publicação não existe.

  4. Se a PartitionGroupsOption propriedade for definida para False, defina-a para True.

  5. Se os artigos que devem compor o registo lógico não existirem, crie uma instância da MergeArticle classe e defina as seguintes propriedades:

    • O nome do artigo para Name.

    • O nome da publicação para PublicationName.

    • (Opcional) Se o artigo for filtrado horizontalmente, especifique a cláusula de filtro de linha para a FilterClause propriedade. Use esta propriedade para especificar um filtro de linha estático ou parametrizado. Para obter mais informações, consulte Filtros de linha parametrizados.

    Para mais informações, consulte Definir um Artigo.

  6. Chame o método Create.

  7. Repita os passos 5 e 6 para cada artigo que compreenda o registo lógico.

  8. Crie uma instância da MergeJoinFilter classe para definir a relação de registo lógico entre artigos. Depois, defina as seguintes propriedades:

  9. Invoque o método AddMergeJoinFilter no objeto que representa o artigo filho na relação. Passa o MergeJoinFilter objeto do passo 8 para definir a relação.

  10. Repita os passos 8 e 9 para cada relação de registo lógico remanescente na publicação.

Exemplo (RMO)

Este exemplo cria um registo lógico que compreende os dois novos artigos para as SalesOrderHeader tabelas e 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