Skip to the content.

Начало

Управление последовательностью

Управление последовательностью решает задачу согласованности данных в процессе регистрации их изменений и последующей доставки, обмена данными, между узлами интеграции по принципу FIFO. Механизм управления последовательностью основан на понятии “векторных часов”. Платформа 1С:Предприятие 8 не имеет адекватных средств для решения этой задачи. Особенно это касается высоко нагруженных и конкурентных условий эксплуатации. Однако, на уровне СУБД этот механизм может быть реализован при помощи специальных объектов ядра - генераторов последовательностей (SEQUENCE).

Важно! Данная технология гарантирует последовательсность согласно очерёдности выполнения команд INSERT при записи в соответствующую таблицу СУБД без учёта последовательности завершения транзакций, в которых они участвуют. Таким образом последовательность гарантируется в разрезе уникальных ключей таблицы СУБД, например, кластерного индекса. Данная технология хорошо подходит для объектного обмена данными или их изменениями, характерного, например, для платформы 1С:Предприятие 8.
На заметку: если требуется обмен данными, основанный на последовательности именно завершённых транзакций (репликация транзакций), то следует использовать технологию CDC (change data capture), которая имеет доступ к LSN (log sequence number) журнала транзакций соответствующей СУБД.

Для управления последовательностью DaJet Script реализует набор команд и функций, которые позволяют работать с объектами SEQUENCE ядра СУБД: создавать и удалять, применять и отзывать для соответствующих объектов 1С:Предприятие 8, а также получать следующее значение последовательности в запросах.

На уровне СУБД счётчик последовательности имеет тип данных bigint.

CREATE SEQUENCE

Команда для создания именованного объекта последовательности базы данных в случае его отсутствия.

CREATE SEQUENCE <identifier>

<identifier> - имя объекта последовательности.

Имя нового объекта последовательности указывается без одинарных кавычек.

Создание последовательности so_my_sequence

USE 'mssql://server/database'
   CREATE SEQUENCE so_my_sequence
END

На уровне СУБД выполняется следующий код:

-- SQL Server
IF NOT EXISTS(SELECT 1 FROM sys.sequences WHERE name = 'so_my_sequence')
BEGIN
   CREATE SEQUENCE my_sequence AS bigint START WITH 1 INCREMENT BY 1;
END;

-- PostgreSQL
CREATE SEQUENCE IF NOT EXISTS so_my_sequence AS bigint INCREMENT BY 1 START WITH 1 CACHE 1;

Наверх

DROP SEQUENCE

Команда для удаления именованного объекта последовательности базы данных. В случае его отсутствия генерируется ошибка СУБД.

DROP SEQUENCE <identifier>

<identifier> - имя объекта последовательности.

Имя удаляемого объекта последовательности указывается без одинарных кавычек.

Удаление последовательности so_my_sequence

USE 'mssql://server/database'
   TRY
      DROP SEQUENCE so_my_sequence
   CATCH
      PRINT ERROR_MESSAGE()
   END -- TRY
END -- USE

На уровне СУБД выполняется следующий код:

-- SQL Server
DROP SEQUENCE so_my_sequence;

-- PostgreSQL
DROP SEQUENCE so_my_sequence;

Наверх

APPLY SEQUENCE

Команда применяет ранее созданную последовательность к указанному измерению, например, регистра сведений, который используется в качестве очереди исходящих сообщений. Работа команды абсолютно прозрачна для 1С:Предприятие 8. Это означает, что код прикладного решения, выполняющего запись в регистр сведений, не меняется. Приращение значений соответствующего измерения будет осуществляться автоматически при добавлении новых записей в коде 1С:Предприятие 8.

Совет: используйте для измерения регистра сведений тип данных ЧИСЛО(15,0).

Команда имеет необязательную опцию RECALCULATE. Эта опция позволяет выполнить пересчёт значений указанного измерения для существующих на данный момент в регистре сведений записей по порядку, начиная с текущего значения последовательности. Оригинальный порядок записей регистра при этом сохраняется таким же, какой он был до начала выполнения команды.

Команда имеет следующий синтаксис:

APPLY SEQUENCE <identifier> ON <table>(<column>) [RECALCULATE]

<identifier> - имя объекта последовательности СУБД.
<table> - полное имя объекта метаданных 1С:Предприятие 8.
<column> - имя реквизита объекта метаданных 1С:Предприятие 8.

Рассмотрим использование команды APPLY SEQUENCE на практическом примере. Допустим, что у нас есть регистр сведений “ИсходящиеСообщения” для регистрации изменений и дальнейшей их передачи во внешние узлы интеграции, имеющий следующую структуру:

Реквизит Назначение Тип данных Описание
НомерСообщения Измерение ЧИСЛО(15,0) Автоматически генерируемый СУБД
последовательный номер сообщения
ТипСообщения Ресурс СТРОКА(1024) Имя типа сообщения
ТелоСообщения Ресурс СТРОКА(0) Тело сообщения в строковом формате,
например, JSON или XML

Теперь выполним следующий код 1С:Предприятие 8:

apply-sequence

В результате выполнения в пустом регистре будет создана одна запись и получена следующая ошибка:

apply-sequence

apply-sequence

Теперь выполним следующий код DaJet Script:

USE 'mssql://server/database'
   CREATE SEQUENCE so_my_sequence -- На всякий случай, если объект последовательности отсутствует
   TRY
      APPLY SEQUENCE so_my_sequence ON РегистрСведений.ИсходящиеСообщения(НомерСообщения)
   CATCH
      PRINT ERROR_MESSAGE()
   END
END

Заново выполним тот же самый код 1С:Предприятие 8 (на этот раз без ошибок), предварительно изменив уже записанное значение измерения “НомерСообщения” в регистре на любое другое отличное от нуля значение. Получим следующий результат:

apply-sequence

На уровне СУБД команда APPLY SEQUENCE выполняет следующий код:

-- SQL Server
IF OBJECT_ID('_inforg134_instead_of_insert', 'TR') IS NULL
EXECUTE('CREATE TRIGGER _inforg134_instead_of_insert ON _InfoRg134 INSTEAD OF INSERT NOT FOR REPLICATION AS
INSERT _InfoRg134(_Fld135, _Fld136, _Fld137)
SELECT NEXT VALUE FOR so_my_sequence, i._Fld136, i._Fld137
FROM INSERTED AS i;');

-- PostgreSQL
CREATE FUNCTION fn_inforg98_before_insert()
RETURNS trigger AS $BODY$
BEGIN
NEW._fld99 := nextval('so_my_sequence');
RETURN NEW;
END $BODY$ LANGUAGE 'plpgsql';

CREATE TRIGGER tr_inforg98_before_insert
BEFORE INSERT ON _inforg98 FOR EACH ROW
EXECUTE PROCEDURE fn_inforg98_before_insert();

В случае использования опции RECALCULATE команды APPLY SEQUENCE дополнительно выполняется следующий код:

-- SQL Server
BEGIN TRANSACTION;
SELECT _Fld135, NEXT VALUE FOR so_my_sequence OVER (ORDER BY _Fld135 ASC) AS sequence_value
INTO #COPY_InfoRg134 FROM _InfoRg134 WITH (TABLOCKX, HOLDLOCK);
UPDATE T SET T._Fld135 = S.sequence_value FROM _InfoRg134 AS T
INNER JOIN #COPY_InfoRg134 AS S ON T._Fld135 = S._Fld135;
DROP TABLE #COPY_InfoRg134;
COMMIT TRANSACTION;

-- PostgreSQL
BEGIN TRANSACTION;
LOCK TABLE _inforg98 IN ACCESS EXCLUSIVE MODE;
WITH cte AS (SELECT _fld99, nextval('so_my_sequence') AS sequence_value
FROM _inforg98 ORDER BY _fld99 ASC)
UPDATE _inforg98 SET _fld99 = cte.sequence_value FROM cte
WHERE _inforg98._fld99 = cte._fld99;
COMMIT TRANSACTION;

Наверх

REVOKE SEQUENCE

Команда отменяет действие соответствующей команды APPLY SEQUENCE. После её успешного выполнения автоматическая генерация значений для указанного ранее измерения регистра сведений 1С:Предприятие 8 прекращается. Объект последовательности не удаляется - для этого следует использовать команду DROP SEQUENCE. Команда имеет следующий синтаксис:

REVOKE SEQUENCE <identifier> ON <table>

<identifier> - имя объекта последовательности СУБД.
<table> - полное имя объекта метаданных 1С:Предприятие 8.

Пример использования команды REVOKE SEQUENCE:

USE 'mssql://server/database'
   TRY
      REVOKE SEQUENCE so_my_sequence ON РегистрСведений.ИсходящиеСообщения
   CATCH
      PRINT ERROR_MESSAGE()
   END
END

На уровне СУБД команда REVOKE SEQUENCE выполняет следующий код:

-- SQL Server
IF OBJECT_ID('_inforg134_instead_of_insert', 'TR') IS NOT NULL
DROP TRIGGER _inforg134_instead_of_insert;

-- PostgreSQL
DROP FUNCTION IF EXISTS fn_inforg98_before_insert CASCADE;
DROP TRIGGER IF EXISTS tr_inforg98_before_insert ON _inforg98;

Наверх

Функция VECTOR

Функция VECTOR возвращает следующее значение счётчика именованной последовательности. Гарантированно, что одно и тоже значение счётчика не может быть получено при повторном вызове этой функции, а также при одновременном вызове параллельно в разных потоках выполнения. Функция VECTOR всегда выполняется в контексте соответствующей базы данных, то есть требует использования “внутри” команды USE. Вызов функции имеет следующий синтаксис:

VECTOR('<sequence_name>')

<sequence_name> - имя объекта последовательности. Имя заключено в одинарные кавычки - это строковой литерал, константа.

1. Обращение к функции VECTOR вне контекста СУБД генерирует ошибку:

DECLARE @current number
SET @current = SELECT VECTOR('so_my_sequence')
-- Результат выполнения скрипта (ошибка):
-- Parent UseStatement is not found

2. Указание несуществующей последовательности генерирует ошибку:

DECLARE @current number
USE 'mssql://server/database'
   SET @current = SELECT VECTOR('so_my_sequence')
END
-- Результат выполнения скрипта (ошибка):
-- SQL Server: Invalid object name 'so_my_sequence'
-- PostgreSQL: relation "so_my_sequence" does not exist

3. Пример целевого использования функции VECTOR (без ошибок):

DECLARE @next    number
DECLARE @current number

USE 'mssql://server/database'

   -- 1. Вариант использования, @current = 0
   SET @current = SELECT VECTOR('so_my_sequence')

   -- 2. Вариант использования
   SELECT VECTOR('so_my_sequence') INTO @current

   -- Два вызова функции: @current = 2, @next = 0
   SET @next = @current + 1

   -- Фиксируем значения в логе программы
   PRINT 'Значения so_my_sequence:'
   PRINT '- текущее   = ' + @current
   PRINT '- следующее = ' + @next

   -- 3. Целевой вариант использования (запишем значение 2)
   INSERT РегистрСведений.ВходящиеСообщения
   SELECT НомерСообщения = @current
        , ТипСообщения   = 'тест'
        , ТелоСообщения  = 'test'

   -- 4. Целевой вариант использования (запишем значение 3)
   INSERT РегистрСведений.ВходящиеСообщения
   SELECT НомерСообщения = VECTOR('so_my_sequence')
        , ТипСообщения   = 'тест'
        , ТелоСообщения  = 'test'
END

-- Результат выполнения скрипта
[2024-10-07 21:46:30] Значения so_my_sequence:
[2024-10-07 21:46:30] - текущее   = 2
[2024-10-07 21:46:30] - следующее = 3

Наверх