Как реализовать генерацию короткого уникального номера в регистре сведений 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке прикладных решений часто возникает задача формирования короткого, удобного для восприятия пользователем номера (например, 8–9 знаков) вместо длинного и сложного строкового GUID (УникальныйИдентификатор). Если данные сохраняются в регистр сведений несколькими пользователями одновременно, простая выборка максимального значения запросом может привести к возникновению дублей и конфликтам блокировок. Разберем основные архитектурные варианты и практические способы безопасного формирования уникальных номеров в 1С.

Архитектурный анализ задачи: документ, справочник или регистр сведений

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

  1. Объекты учета с событиями и историей: Если сущность представляет собой отдельный бизнес-процесс (например, заказ), правильнее использовать объект метаданных Документ. Документы платформы уже имеют встроенный оптимизированный механизм автонумерации, контроль уникальности и поддержку транзакционной записи.
  2. Справочники: Если сущность представляет собой список однотипных объектов, подходящим решением будет Справочник, где также реализована встроенная автонумерация кодов.
  3. История изменения статусов: Если нам требуется хранить срез последних статусов заказа, оптимальным вариантом является периодический РегистрСведений. В этом случае измерением выступает ссылка на документ (или справочник), а ресурсом — значение статуса. При такой схеме генерировать искусственный уникальный номер для каждой записи регистра не требуется, так как уникальность обеспечивается комбинацией измерений и периода.

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

Способ 1. Использование служебного справочника-нумератора

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

Разберем по шагам логику работы этого метода:

  1. В метаданных создаем справочник, у которого длина наименования равна 0, а тип кода настроен как Число с автонумерацией и контролем уникальности.
  2. При создании новой записи в регистре сведений программно создается новый элемент данного справочника.
  3. Платформа автоматически назначает элементу следующий по порядку номер в рамках короткой транзакции.
  4. Полученный код (или ссылка на элемент) записывается в измерение регистра сведений.

Посмотрим на пример программной генерации номера через вспомогательный справочник:


Функция ПолучитьНовыйНомерЧерезСправочник()
    
    НовыйЭлемент = Справочники.СлужебныеНомера.СоздатьЭлемент();
    НовыйЭлемент.Записать();
    
    Возврат НовыйЭлемент.Код;
    
КонецФункции

Способ 2. Паттерн генератора последовательностей на регистре сведений с управляемой блокировкой

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

Выясним причину возможных проблем при обычном получении максимума: если два сеанса одновременно прочитают максимальный номер, они получат одинаковое число и попытаются записать одну и ту же запись. Чтобы этого избежать, используем объект БлокировкаДанных в режиме РежимБлокировкиДанных.Исключительный строго по имени счетчика.

Разберем структуру регистра Счетчики:

  1. Измерение: ИмяСчетчика (Тип: Строка, длина 50).
  2. Ресурс: Значение (Тип: Число, длина 10, точность 0).

Рассмотрим подробнее программный код получения очередного номера в короткой транзакции:


Функция ПолучитьСледующийНомер(ИмяСчетчика)
    
    НачатьТранзакцию();
    Попытка
        Блокировка = Новый БлокировкаДанных;
        ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.Счетчики");
        ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
        ЭлементБлокировки.УстановитьЗначение("ИмяСчетчика", ИмяСчетчика);
        Блокировка.Заблокировать();
        
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |	Счетчики.Значение КАК Значение
            |ИЗ
            |	РегистрСведений.Счетчики КАК Счетчики
            |ГДЕ
            |	Счетчики.ИмяСчетчика = &ИмяСчетчика";
        Запрос.УстановитьПараметр("ИмяСчетчика", ИмяСчетчика);
        
        Выборка = Запрос.Выполнить().Выбрать();
        
        Если Выборка.Следующий() Тогда
            ТекущееЗначение = Выборка.Значение;
        Иначе
            ТекущееЗначение = 0;
        КонецЕсли;
        
        СледующийНомер = ТекущееЗначение + 1;
        
        МенеджерЗаписи = РегистрыСведений.Счетчики.СоздатьМенеджерЗаписи();
        МенеджерЗаписи.ИмяСчетчика = ИмяСчетчика;
        МенеджерЗаписи.Значение = СледующийНомер;
        МенеджерЗаписи.Записать(Истина);
        
        ЗафиксироватьТранзакцию();
        
        Возврат СледующийНомер;
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение;
    КонецПопытки;
    
КонецФункции

Важный момент: Управляемая блокировка накладывается исключительно на одну строку в служебном регистре Счетчики. Целевой регистр со статусами не блокируется, а параллельные фоновые задания могут свободно читать из него данные без ожидания освобождения блокировок (при использовании версионирования строк на уровне СУБД).

Способ 3. Пакетное резервирование диапазонов номеров (подход БСП)

В высоконагруженных системах, где частота записи высока, последовательное обращение к единому счетчику может вызывать очередь ожиданий. В Библиотеке Стандартных Подсистем (БСП) реализован механизм пакетного выделения идентификаторов (функция РаботаСФайламиСлужебный.ПолучитьОчереднойИдентификаторОбъекта()).

Принцип пакетного резервирования строится следующим образом:

  1. Сеанс обращается к счетчику и увеличивает его не на 1, а на величину пула (например, сразу на 50 или 100 номеров).
  2. Выделенный диапазон номеров сохраняется в памяти сеанса (в параметрах сеанса или модуле).
  3. Последующие операции в текущем сеансе разбирают зарезервированные номера локально без дополнительных обращений к базе данных и без транзакционных блокировок.
  4. Когда локальный пул исчерпан, сеанс запрашивает следующий диапазон.

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

Почему не следует использовать обрезку GUID и математическое хеширование

Попытка получить 8–9-значный номер путем обрезания или хеширования УникальныйИдентификатор (например, взятие остатка от деления или первых разрядов хеша) не гарантирует уникальность. Вследствие «парадокса дней рождения» в пространстве из 9 знаков (1 000 000 000 значений) вероятность коллизии (совпадения номеров) резко возрастает уже после генерации первых десятков тысяч записей, что приведет к перезаписи существующих данных или ошибкам уникальности.

← На главную