Поделиться через


Определение связи логических записей между статьями таблиц слияния

Область применения: SQL Server

В этом разделе описывается, как определить связь логических записей между статьями таблицы слияния в SQL Server с помощью SQL Server Management Studio, Transact-SQL или объектов управления репликацией (RMO).

Репликация слиянием позволяет определить связь между связанными строками в различных таблицах. Эти строки затем могут быть обработаны во время синхронизации как элементы транзакции. Логическая запись может быть определена между двумя статьями независимо от наличия связи фильтров соединения между ними. Дополнительные сведения см. в статье Группирование изменений в связанных строках с помощью логических записей.

Примечание.

Эта функция будет удалена в будущей версии SQL Server. Избегайте использования этого компонента в новых разработках и запланируйте изменение существующих приложений, в которых он применяется.

В этом разделе

Перед началом

Ограничения

  • Если добавление, изменение или удаление логической записи выполняется после инициализации подписок на публикацию, следует создать новый моментальный снимок и повторно инициализировать все подписки после внесения изменений. Дополнительные сведения о требованиях к изменениям свойств см. в статье Изменение свойств публикации и статьи.

Использование среды SQL Server Management Studio

Определите логические записи в диалоговом окне "Добавление соединения", которое доступно в мастере создания публикаций и в диалоговом окне "Свойства публикации — <публикация>". Дополнительные сведения об использовании мастера и доступе к этому диалоговому окну см. в статьях Создание публикации и Просмотр и изменение свойств публикации.

Логические записи могут определяться в диалоговом окне Добавление соединения , только если они применяются к фильтру соединения в публикации слиянием и если публикация соответствует требованиям для использования предварительно вычисляемых секций. Чтобы определить логические записи, которые не применяются к фильтрам соединения, и чтобы установить обнаружение и разрешение конфликтов на уровне логических записей, следует использовать хранимые процедуры.

Определение связи логических записей

  1. На странице "Строки таблицы фильтра" мастера создания публикации или страницы "Строки фильтра" диалогового окна "Свойства публикации" <— "Публикация>" выберите фильтр строк в области "Отфильтрованные таблицы".

    Связь логических записей ассоциирована с фильтром соединения, который расширяет фильтр строк. Поэтому необходимо определить фильтр строк, прежде чем можно будет расширить фильтр с помощью фильтра соединения и применить связь логических записей. После того как определен один фильтр соединения, можно расширить этот фильтр соединения с помощью еще одного фильтра соединения. Дополнительные сведения об определении фильтров соединения см. в разделе Определение и изменение фильтра соединения между статьями публикации слиянием.

  2. Щелкните Добавитьи выберите Добавить соединение для расширения выбранного фильтра.

  3. Определите фильтр соединения в диалоговом окне Добавление соединения , а затем установите флажок Логическая запись.

  4. Если вы находитесь в диалоговом окне "Свойства публикации — <публикация> ", нажмите кнопку "ОК ", чтобы сохранить и закрыть диалоговое окно.

Удаление связи логических записей

  • Удалите лишь связь логических записей или удалите и связь логических записей, и ассоциированный с ней фильтр соединения.

    Чтобы удалить только связь логических записей.

    1. На странице "Строки фильтра" мастера создания публикации или страницы "Строки фильтра" диалогового окна "Свойства публикации — публикация>" <выберите фильтр соединения, связанный с связью логической записи в области "Отфильтрованные таблицы", а затем нажмите кнопку "Изменить".

    2. В диалоговом окне Изменить соединение снимите флажок Логическая запись.

    3. Нажмите ОК.

    Чтобы удалить связь логических записей и фильтр соединения, ассоциированный с этой связью, выполните следующие действия.

    • На странице "Фильтры строк" мастера создания публикации или свойств публикации — "Публикация>" <выберите фильтр в области "Отфильтрованные таблицы" и нажмите кнопку "Удалить". Если удаляемый фильтр соединения расширен за счет других фильтров, эти фильтры также будут удалены.

Использование Transact-SQL

С помощью хранимых процедур репликации можно программно задать связь логических записей между статьями.

Определение связи логических записей без сопутствующего фильтра соединения

  1. Если публикация содержит фильтруемые статьи, выполните хранимую процедуру sp_helpmergepublicationи проверьте значение параметра use_partition_groups в результирующем наборе.

    • Если значение равно 1, то предварительно вычисляемые секции уже используются.

    • Если значение равно 0, выполните хранимую процедуру sp_changemergepublication на издателе в базе данных публикации. Задайте use_partition_groups в качестве значения параметра @property и true в качестве значения параметра @value.

      Примечание.

      Если публикация не поддерживает предварительно вычисляемые секции, то логические записи использовать нельзя. Дополнительные сведения см. в статье Оптимизация производительности параметризованного фильтра с помощью предварительно вычисляемых секций.

    • Если это значение NULL, то необходимо запустить агент моментальных снимков для создания исходного моментального снимка для публикации.

  2. Если статьи, входящие в логическую запись, не существуют, выполните хранимую процедуру sp_addmergearticle на издателе в базе данных публикации. Укажите один из приведенных ниже параметров определения и распознавания конфликтов в логической записи.

    • Для распознавания и разрешения конфликтов, возникающих в связанных строках логической записи, присвойте значение true параметрам @logical_record_level_conflict_detection и @logical_record_level_conflict_resolution.

    • Для использования стандартного определения и разрешения конфликтов уровня строки и столбца присвойте значение false параметрам @logical_record_level_conflict_detection и @logical_record_level_conflict_resolution(они имеют это значение по умолчанию).

  3. Повторите шаг 2 для каждой статьи, которая содержит логическую запись. Необходимо использовать в каждой статье логической записи одинаковые параметры определения и разрешения конфликтов. Дополнительные сведения см. в статье Распознавание и разрешение конфликтов в логических записях.

  4. На издателе в базе данных публикации выполните хранимую процедуру sp_addmergefilter. Укажите @publication, имя одной статьи связи в качестве значения параметра @article, имя второй статьи в качестве значения параметра @join_articlename, имя связи в качестве значения параметра @filtername, предложение, определяющее связь между двумя статьями в качестве значения параметра @join_filterclause, тип соединения в качестве значения параметра @join_unique_key , а также одно из следующих значений параметра @filter_type:

    • 2 — определяет логическую связь;

    • 3 — определят логическую связь с фильтром соединения.

    Примечание.

    Фильтр соединения не используется, направление связи между двумя статьями несущественно.

  5. Повторите шаг 2 для всех оставшихся связей логических записей в публикации.

Изменение способа определения и разрешения конфликтов логических записей

  1. Для определения и разрешения конфликтов, возникающих в связанных строках логической записи, выполните следующие действия.

    • В базе данных публикации на издателе выполните процедуру sp_changemergearticle. Присвойте параметру @property значение logical_record_level_conflict_detection , а параметру @value — значение true. Укажите значение 1 для параметров @force_invalidate_snapshot и @force_reinit_subscription.

    • В базе данных публикации на издателе выполните процедуру sp_changemergearticle. Присвойте параметру @property значение logical_record_level_conflict_resolution , а параметру @value — значение true. Укажите значение 1 для параметров @force_invalidate_snapshot и @force_reinit_subscription.

  2. Для использования стандартного метода определения и разрешения конфликтов уровня строки и столбца выполните следующие действия.

    • В базе данных публикации на издателе выполните процедуру sp_changemergearticle. Укажите в параметре @property значение logical_record_level_conflict_detection , а в параметре @value — значение false. Укажите значение 1 для параметров @force_invalidate_snapshot и @force_reinit_subscription.

    • В базе данных публикации на издателе выполните процедуру sp_changemergearticle. Присвойте параметру @property значение logical_record_level_conflict_resolution , а параметру @value — значение false. Укажите значение 1 для параметров @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;
    

    Запомните имя удаляемой связи логических записей, содержащееся в столбце filtername результирующего набора.

    Примечание.

    Этот запрос возвращает те же сведения, что и системная хранимая процедура sp_helpmergefilter, однако системная хранимая процедура возвращает только сведения по тем связям логических записей, которые являются также фильтрами соединения.

  2. На издателе в базе данных публикации выполните хранимую процедуру sp_dropmergefilter. Укажите параметр @publication, имя одной из статей в связи в качестве значения параметра @article, а также имя связи из шага 1 в качестве значения параметра @filtername.

Пример (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.

Определение связи логических записей без сопутствующего фильтра соединения

  1. Создайте соединение с издателем с помощью класса ServerConnection .

  2. Создайте экземпляр класса MergePublication , установите для публикации свойства Name и DatabaseName , а также установите созданное на шаге 1 соединение в качестве значения свойства ConnectionContext .

  3. Чтобы получить свойства объекта, вызовите метод LoadProperties . Если этот метод возвращает false, то либо на шаге 2 были неверно определены свойства публикации, либо публикация не существует.

  4. Если свойство PartitionGroupsOption имеет значение False, укажите значение True.

  5. Если статьи, которые должны составить логическую запись, не существуют, создайте экземпляр класса MergeArticle и задайте следующие свойства.

    • Имя статьи для Name.

    • имя публикации в качестве значения параметра PublicationName.

    • Если статья отфильтрована горизонтально, задайте условие фильтра строк для свойства FilterClause (необязательно). Используйте это свойство для определения статического или параметризованного фильтра строк. Дополнительные сведения см. в разделе Параметризованные фильтры строк.

    Дополнительные сведения см. в статье определить статью.

  6. Вызовите метод Create .

  7. Повторите шаги 5 и 6 для каждой статьи, составляющей логическую запись.

  8. Создайте экземпляр класса MergeJoinFilter , чтобы определить связь логических записей между статьями. Затем установите следующие свойства.

  9. Вызовите метод AddMergeJoinFilter для объекта, представляющего дочернюю статью в этой связи. Передайте объект MergeJoinFilter из шага 8, чтобы определить связь.

  10. Повторите шаги 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