Как избежать дублей при генерации уникальных номеров в нетиповых полях 1С?

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

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

Почему возникают дубли при работе с Регистром Сведений?

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

Также ошибкой является использование СоздатьМенеджерЗаписи без предварительного считывания существующей записи или отсутствие ключевых полей. В таком случае платформа пытается добавить новую строку вместо обновления существующей, что при отсутствии жестких ограничений в структуре регистра приводит к появлению «мусора» и некорректному считыванию максимума.

Способ 1: Использование числового типа данных и простая логика

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


// Вместо сложной функции формирования строки
// Используем числовой формат для записи в регистр
НовыйНомер = СтарыйНомер + 1;
Источник.НомерТ = Формат(НовыйНомер, "ЧЦ=13; ЧВН=; ЧГ=0");

Способ 2: Корректная блокировка записи

Чтобы гарантированно избежать дублей, необходимо жестко ограничить доступ к записи регистра в момент её изменения. Разберем шаги правильного алгоритма:

  1. Открытие транзакции: Все действия по получению и записи номера должны происходить внутри явной транзакции.
  2. Установка блокировки: Если регистр не имеет измерений, добавьте фиктивное измерение с константным значением (например, «Основной»). Это позволит платформе поставить Исключительную блокировку на конкретный набор ключей.
  3. Обновление записи: Используйте НаборЗаписей с отбором по измерению вместо постоянного создания новых менеджеров записи.

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


НачатьТранзакцию();
Попытка
    Блокировка = Новый БлокировкаДанных;
    Элемент = Блокировка.Добавить("РегистрСведений.ПоследнийНомерТ");
    Элемент.УстановитьЗначение("ИзмерениеКлюч", "Основной");
    Блокировка.Заблокировать();
    
    // Здесь выполняем чтение последнего номера
    // И последующую запись нового значения через НаборЗаписей
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

Способ 3: Отказ от регистра в пользу константы

Если для нумерации вам нужен только один глобальный счетчик, использование РегистрСведений является избыточным. Попробуйте перевести функционал на Константы. Константа имеет ряд преимуществ:

Важные замечания по производительности и уровню изоляции

Помните, что в современных клиент-серверных вариантах работы (особенно при использовании Read Committed Snapshot) обычное чтение может игнорировать установленные блокировки других сеансов. Поэтому:

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

← На главную