Как записывать повторяющиеся данные в регистр сведений 1С и избежать ошибки уникальности

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

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

Разберем причину возникновения ошибки

Для начала проанализируем ситуацию с технической точки зрения. Регистр сведений — это прикладной объект, предназначенный для хранения информации в разрезе определенных измерений. Ключевым моментом здесь является понятие ключа записи. Ключ записи в независимом регистре сведений формируется из совокупности всех его измерений, а если регистр периодический — то из комбинации Период + Измерения.

Важно понимать: значения ресурсов и реквизитов не входят в ключ уникальности. Это означает, что если в вашем регистре есть измерение Сотрудник и ресурс Оклад, вы не сможете записать две строки по одному сотруднику на одну и ту же дату, даже если суммы окладов будут разными. Платформа 1С на уровне СУБД создает уникальный индекс по измерениям, и любая попытка записать дубль блокируется на уровне базы данных.

Решение 1: Использование дополнительного измерения «НомерЗаписи»

Один из самых простых способов, предложенных в обсуждении, — это добавление вспомогательного измерения, которое будет гарантировать уникальность комбинации. Рассмотрим, как это реализовать на практике. Мы создаем измерение НомерЗаписи с типом Число.

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


// Пример получения нового номера записи в пределах дня
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   МАКСИМУМ(МойРегистр.НомерЗаписи) КАК МаксНомер
    |ИЗ
    |   РегистрСведений.МойРегистр КАК МойРегистр
    |ГДЕ
    |   МойРегистр.Период = &Период
    |   И МойРегистр.Объект = &Объект";

Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("Объект", СсылкаНаОбъект);

Результат = Запрос.Выполнить().Выбрать();
НовыйНомер = 1;
Если Результат.Следующий() Тогда
    НовыйНомер = Результат.МаксНомер + 1;
КонецЕсли;

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

Решение 2: Точная метка времени и микросекунды

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

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

Рассмотрим пример логики:

  1. Берем текущую метку времени.
  2. Если для данной секунды запись уже была произведена в этой транзакции, увеличиваем значение миллисекунд или добавляем техническое измерение-счетчик.
  3. Это сохраняет хронологический порядок записей, что критично для журналов действий.

Решение 3: Уникальный идентификатор (GUID) — самый надежный метод

Если порядок записей в пределах одной секунды не имеет критического значения, а важна лишь возможность сохранить все входящие данные, наиболее профессиональным подходом будет использование Уникального Идентификатора (GUID).

Добавим в регистр измерение ИдентификаторЗаписи с типом Строка (36 символов) или УникальныйИдентификатор. Теперь при записи каждой новой строки мы будем генерировать новый ГУИД. Посмотрим, как это выглядит в коде:


МенеджерЗаписи = РегистрыСведений.МойРегистр.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Период = ТекущаяДата();
МенеджерЗаписи.Объект = СсылкаНаОбъект;
// Генерируем уникальный ключ, который никогда не повторится
МенеджерЗаписи.ИдентификаторЗаписи = Новый УникальныйИдентификатор(); 
МенеджерЗаписи.РесурсЗначение = 100;
МенеджерЗаписи.Записать();

Преимущества данного метода:

Сравнение с регистром накопления

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

Однако у регистра накопления есть обязательное условие: наличие регистратора. Каждая запись должна быть привязана к документу. Если ваша задача — просто хранить технические логи или внешние данные, создание документов под каждую загрузку действительно может привести к «раздуванию» базы данных и лишним накладным расходам на проведение.

Тем не менее, если данных очень много (миллионы строк), регистр накопления с одним техническим документом-регистратором может работать быстрее на чтение итогов, чем огромный регистр сведений.

Когда стоит использовать справочник вместо регистра?

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

Если вам не нужна периодичность (хранение истории изменений во времени), и вы просто хотите хранить список событий, возможно, стоит создать справочник, где наименование или код будут содержать дату, а реквизиты — ваши данные. Однако помните, что справочники — это более «тяжелые» объекты для базы данных по сравнению с записями регистров сведений.

Итоги и рекомендации

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

  1. Если записи действительно могут повторяться в одну секунду и вам важен их порядок — добавляйте измерение НомерЗаписи (типа Число) или Микросекунды.
  2. Если порядок не важен, а важна уникальность любой ценой — используйте измерение с типом УникальныйИдентификатор.
  3. Всегда проверяйте, не является ли ваша задача хранением истории объекта. Если да, то, возможно, стоит пересмотреть состав измерений, чтобы они действительно описывали уникальное состояние объекта в конкретный момент времени.
  4. Избегайте использования регистра сведений для хранения бесконечных логов без механизма очистки, так как отсутствие регистратора затрудняет управление объемом данных.

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

← На главную