Definire una relazione tra record logici degli articoli di tabelle di merge

Si applica a: SQL Server

Questo argomento descrive come definire una relazione tra record logici tra articoli di tabella del merge in SQL Server tramite SQL Server Management Studio, Transact-SQL o Replication Management Objects (RMO).

La replica di tipo merge consente di definire una relazione tra righe correlate in tabelle diverse. Queste righe possono quindi essere elaborate come un'unità transazionale durante la sincronizzazione. È possibile definire un record logico tra due articoli indipendentemente dal fatto che per essi sia stata definita una relazione tra filtri di join. Per altre informazioni, vedere Raggruppare modifiche alle righe correlate con record logici.

Nota

Questa funzionalità verrà rimossa nelle versioni future di SQL Server. Evitare di usare questa funzionalità in un nuovo progetto di sviluppo e prevedere interventi di modifica nelle applicazioni in cui è attualmente implementata.

Contenuto dell'articolo

Prima di iniziare

Limitazioni e restrizioni

  • Se si aggiunge, modifica o elimina un record logico dopo che sono state inizializzate sottoscrizioni per la pubblicazione, è necessario generare un nuovo snapshot e reinizializzare tutte le sottoscrizioni in seguito alla modifica. Per altre informazioni sui requisiti per la modifica delle proprietà, vedere Modificare le proprietà di pubblicazioni e articoli.

Utilizzo di SQL Server Management Studio

Definire record logici nella finestra di dialogo Aggiungi join disponibile nella Creazione guidata nuova pubblicazione e nella finestra di dialogo Proprietà pubblicazione - <Pubblicazione>. Per altre informazioni sull'uso della creazione guidata e l'accesso alla finestra di dialogo, vedere Creare una pubblicazione e Visualizzare e modificare le proprietà della pubblicazione.

È possibile definire record logici nella finestra di dialogo Aggiungi join solo se vengono applicati a un filtro join di una pubblicazione di tipo merge e la pubblicazione soddisfa i requisiti per l'utilizzo di partizioni pre-calcolate. Per definire record logici che non vengono applicati a filtri di join e per impostare il rilevamento e la risoluzione dei conflitti a livello di record logici, è necessario utilizzare le stored procedure.

Per definire una relazione tra record logici

  1. Nella pagina Filtro righe tabella della Creazione guidata nuova pubblicazione o nella pagina Filtro righe della finestra di dialogo Proprietà pubblicazione - <Pubblicazione> selezionare un filtro di riga nel riquadro Tabelle filtrate.

    A una relazione tra record logici è associato un filtro join che estende un filtro di riga. È pertanto necessario definire un filtro di riga prima di poter estendere il filtro con un join e applicare una relazione tra record logici. Dopo aver definito un filtro join, è possibile estenderlo con un altro filtro join. Per ulteriori informazioni sulla definizione di filtri join, vedere Definizione e modifica di un filtro di join tra articoli di merge.

  2. Fare clic su Aggiungie quindi su Aggiungi join per estendere il filtro selezionato.

  3. Nella finestra di dialogo Aggiungi join definire un filtro join e quindi selezionare la casella di controllo Record logico.

  4. Se è visualizzata la finestra di dialogo Proprietà pubblicazione - <Pubblicazione> fare clic su OK per salvare e chiudere la finestra di dialogo.

Per eliminare una relazione tra record logici

  • Eliminare solo la relazione tra record logici oppure eliminare la relazione e il filtro join ad essa associato.

    Per eliminare solo la relazione tra record logici:

    1. Nella pagina Filtro righe della Creazione guidata nuova pubblicazione o nella pagina Filtro righe della finestra di dialogo Proprietà pubblicazione - <Pubblicazione> selezionare il filtro join associato alla relazione tra record logici nel riquadro Tabelle filtrate e quindi fare clic su Modifica.

    2. Nella finestra di dialogo Modifica join deselezionare la casella di controllo Record logico.

    3. Seleziona OK.

    Per eliminare la relazione tra record logici e il filtro join ad essa associato:

    • Nella pagina Filtro righe della Creazione guidata nuova pubblicazione o nella finestra di dialogo Proprietà pubblicazione - <Pubblicazione> selezionare un filtro nel riquadro Tabelle filtrate e quindi fare clic su Elimina. Se il filtro di join eliminato è esteso da altri join, anch'essi verranno eliminati.

Utilizzo di Transact-SQL

Per specificare a livello di programmazione relazioni tra record logici tra gli articoli, è possibile utilizzare stored procedure di replica.

Per definire una relazione tra record logici senza un filtro di join associato

  1. Se la pubblicazione contiene eventuali articoli con filtro, eseguire sp_helpmergepublicatione notare il valore di use_partition_groups nel set di risultati.

    • Se il valore è 1, le partizioni calcolate vengono già utilizzate.

    • Se il valore è 0, eseguire sp_changemergepublication nel database di pubblicazione del server di pubblicazione. Specificare il valore use_partition_groups per @property e il valore true per @value.

      Nota

      Se la pubblicazione non supporta le partizioni calcolate, non sarà possibile utilizzare i record logici. Per altre informazioni vedere "Requisiti per l'uso delle partizioni pre-calcolate" nell'argomento Ottimizzare le prestazioni dei filtri con parametri con le partizioni pre-calcolate.

    • Se il valore è NULL, è necessario eseguire l'agente snapshot per generare lo snapshot iniziale per la pubblicazione.

  2. Se gli articoli che includeranno il record logico non esistono, eseguire sp_addmergearticle nel database di pubblicazione del server di pubblicazione. Specificare una delle opzioni di rilevamento e risoluzione dei conflitti seguenti per il record logico:

    • Per rilevare e risolvere conflitti che si verificano all'interno di righe correlate del record logico, specificare il valore true per @logical_record_level_conflict_detection e @logical_record_level_conflict_resolution.

    • Per usare la funzionalità standard di rilevamento e risoluzione dei conflitti a livello di riga o di colonna, specificare il valore false per @logical_record_level_conflict_detection e @logical_record_level_conflict_resolution, che corrisponde all'impostazione predefinita.

  3. Ripetere il passaggio 2 per ogni articolo che includerà il record logico. È necessario utilizzare la stessa opzione di rilevamento e risoluzione dei conflitti per ogni articolo del record logico. Per altre informazioni, vedere Rilevamento e risoluzione dei conflitti nei record logici.

  4. Nel database di pubblicazione del server di pubblicazione eseguire sp_addmergefilter. Specificare @publication, il nome di un articolo della relazione per @article, il nome del secondo articolo per @join_articlename, un nome per la relazione per @filtername, una clausola che definisce la relazione tra i due articoli per @join_filterclause, il tipo di join per @join_unique_key e uno dei valori seguenti per @filter_type:

    • 2 : consente di definire una relazione logica.

    • 3 : consente di definire una relazione logica con un filtro join.

    Nota

    Se il filtro join non viene utilizzato, la direzione della relazione tra i due articoli non ha rilevanza.

  5. Ripetere il passaggio 2 per tutte le altre relazioni tra record logici incluse nella pubblicazione.

Per modificare l'opzione di rilevamento e risoluzione dei conflitti per i record logici

  1. Per rilevare e risolvere conflitti che si verificano all'interno di righe correlate nel record logico:

    • Nel database di pubblicazione del server di pubblicazione eseguire sp_changemergearticle. Specificare il valore logical_record_level_conflict_detection per @property e il valore true per @value. Specificare il valore 1 per @force_invalidate_snapshot e @force_reinit_subscription.

    • Nel database di pubblicazione del server di pubblicazione eseguire sp_changemergearticle. Specificare il valore logical_record_level_conflict_resolution per @property e il valore true per @value. Specificare il valore 1 per @force_invalidate_snapshot e @force_reinit_subscription.

  2. Per utilizzare l'opzione standard di rilevamento e risoluzione dei conflitti a livello di riga o di colonna:

    • Nel database di pubblicazione del server di pubblicazione eseguire sp_changemergearticle. Specificare il valore logical_record_level_conflict_detection per @property e il valore false per @value. Specificare il valore 1 per @force_invalidate_snapshot e @force_reinit_subscription.

    • Nel database di pubblicazione del server di pubblicazione eseguire sp_changemergearticle. Specificare il valore logical_record_level_conflict_resolution per @property e il valore false per @value. Specificare il valore 1 per @force_invalidate_snapshot e @force_reinit_subscription.

Per rimuovere una relazione tra record logici

  1. Nel database di pubblicazione del server di pubblicazione eseguire la query seguente per restituire informazioni su tutte le relazioni tra record logici definite per la pubblicazione specificata:

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

    Si noti il nome della relazione tra record logici da rimuovere nella colonna filtername del set di risultati.

    Nota

    Questa query restituisce le stesse informazioni di sp_helpmergefilter. La stored procedure di sistema restituisce tuttavia solo le informazioni sulle relazioni tra record logici che corrispondono anche a filtri join.

  2. Nel database di pubblicazione del server di pubblicazione eseguire sp_dropmergefilter. Specificare @publication, il nome di uno degli articoli della relazione per @articlee il nome della relazione ottenuto al passaggio 1 per @filtername.

Esempio (Transact-SQL)

In questo esempio le partizioni pre-calcolate vengono abilitate in una pubblicazione esistente e viene creato un record logico che comprende i due nuovi articoli per le tabelle 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

Utilizzo di RMO (Replication Management Objects)

Nota

La replica di tipo merge consente di specificare che i conflitti vengano rilevati e risolti a livello di record logico. Queste opzioni tuttavia non possono essere impostate tramite RMO.

Per definire una relazione tra record logici senza un filtro di join associato

  1. Creare una connessione al server di pubblicazione tramite la classe ServerConnection .

  2. Creare un'istanza della classe MergePublication , impostare le proprietà Name e DatabaseName per la pubblicazione, quindi impostare la proprietà ConnectionContext sulla connessione creata al passaggio 1.

  3. Chiamare il metodo LoadProperties per recuperare le proprietà dell'oggetto. Se questo metodo restituisce false, le proprietà della pubblicazione sono state definite in modo non corretto nel passaggio 2 oppure la pubblicazione non esiste.

  4. Se la proprietà PartitionGroupsOption è impostata su False, impostarla su True.

  5. Se gli articoli che dovranno includere il record logico non esistono, creare un'istanza della classe MergeArticle e impostare le proprietà seguenti:

    • Nome dell'articolo per Name.

    • Il nome della pubblicazione per PublicationName.

    • (Facoltativo) Se l'articolo è filtrato orizzontalmente, specificare la clausola del filtro di riga per la proprietà FilterClause . Utilizzare questa proprietà per specificare un filtro di riga statico o con parametri. Per altre informazioni sui filtri di riga con parametri, vedere Filtri di riga con parametri.

    Per altre informazioni, vedere definire un articolo.

  6. Chiamare il metodo Create .

  7. Ripetere i passaggi 5 e 6 per ogni articolo che includerà il record logico.

  8. Creare un'istanza della classe MergeJoinFilter per definire la relazione tra record logici tra gli articoli. Impostare quindi le proprietà seguenti:

  9. Chiamare il metodo AddMergeJoinFilter sull'oggetto che rappresenta l'articolo figlio nella relazione. Passare l'oggetto MergeJoinFilter indicato nel passaggio 8 per definire la relazione.

  10. Ripetere i passaggi 8 e 9 per tutte le altre relazioni tra record logici incluse nella pubblicazione.

Esempio (RMO)

In questo esempio viene creato un record logico che comprende i due nuovi articoli per le tabelle SalesOrderHeader 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