Почему в периодическом регистре сведений записывается только одна запись и как это исправить?

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

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

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

Понимание природы периодических регистров сведений

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

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

Анализ исходной проблемы: почему записи перезаписываются?

Автор исходного запроса столкнулся с проблемой, используя следующий код для записи в периодический регистр сведений ЖурналОбменаССайтом:


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

Ключевая ошибка в данном случае заключается в неправильном использовании объекта РегистрСведенийНаборЗаписей (далее просто НаборЗаписей). Давайте разберем его поведение, которое привело к перезаписи:

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

Правильные способы записи в регистры сведений

Для эффективной и корректной работы с регистрами сведений в 1С существуют два основных объекта, каждый из которых имеет свою специфику применения:

  1. РегистрСведенийМенеджерЗаписи (МенеджерЗаписи): Предназначен для работы с одной конкретной записью.
  2. РегистрСведенийНаборЗаписей (НаборЗаписей): Предназначен для работы с коллекцией записей.

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

Решение 1: Использование МенеджераЗаписи (рекомендуемый подход для одной записи)

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

Разберем по шагам, как правильно использовать МенеджерЗаписи:

  1. Создание объекта МенеджерЗаписи: Получаем менеджер записи, вызывая статический метод СоздатьМенеджерЗаписи() у объекта регистра сведений.
  2. МенеджерЗаписи = РегистрыСведений.ЖурналОбменаССайтом.СоздатьМенеджерЗаписи();

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


// Заполняем поле Период и измерения регистра
МенеджерЗаписи.Период = ТекущаяДата(); 
МенеджерЗаписи.ОписаниеСообщения = ОписаниеОшибки;
МенеджерЗаписи.ДействиеИсточник = ДействиеИсточник;
МенеджерЗаписи.ТипСообщения = ТипСообщения;

// Если у регистра есть ресурсы (например, для хранения каких-либо значений),
// их также необходимо заполнить:
// МенеджерЗаписи.ИмяРесурса = ЗначениеРесурса;

Важный нюанс: В отличие от НаборЗаписей, где вы сначала добавляете объект записи в набор (НаборЗаписей.Добавить()), МенеджерЗаписи *сам по себе является* этой записью. Поэтому здесь нет необходимости вызывать метод Добавить().

МенеджерЗаписи.Записать();

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

Решение 2: Использование НабораЗаписей с отбором (для пакетных операций)

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

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

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

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


МенеджерНабора.Прочитать(); // Считываем все существующие записи (или записи по отбору) в наш объект

Теперь ваш объект МенеджерНабора содержит все записи, которые были в регистре (или те, что соответствовали отбору). Только после этого можно безопасно добавлять новые записи или изменять существующие.


ЗаписьНов = МенеджерНабора.Добавить();
ЗаписьНов.Период = ТекущаяДата();
ЗаписьНов.ОписаниеСообщения = ОписаниеОшибки;
ЗаписьНов.ДействиеИсточник = ДействиеИсточник;
ЗаписьНов.ТипСообщения = ТипСообщения;

МенеджерНабора.Записать(); // Теперь в регистр запишутся все старые записи, которые были прочитаны, плюс наша новая запись

Резюме по НаборЗаписей: Использование НаборЗаписей без Отбора и без предварительного вызова Прочитать() всегда приводит к полной очистке регистра и записи только тех данных, которые были вручную добавлены в НаборЗаписей. Этот подход редко является желаемым, если речь не идет о полном и контролируемом сбросе содержимого регистра.

Учет периодичности "в пределах секунды" и функции ТекущаяДата()

Автор в своем вопросе справедливо упомянул о возможной проблеме с функцией ТекущаяДата() при периодичности "в пределах секунды". Хотя это не было основной причиной перезаписи (основной проблемой был НаборЗаписей без отбора), этот аспект также важен для понимания.

  1. Точность ТекущаяДата(): Функция ТекущаяДата() возвращает текущую дату и время с точностью до миллисекунд.
  2. Конфликт при одинаковых миллисекундах: Если ваш регистр имеет периодичность "в пределах секунды", и вы записываете несколько записей в течение одной секунды, но с разными миллисекундами, система будет считать их уникальными (при условии уникальности остальных измерений). Однако, если две последовательные операции записи (для одного и того же набора измерений) произойдут настолько быстро, что ТекущаяДата() вернет абсолютно идентичные значения (с точностью до миллисекунд), то вторая запись перезапишет первую, даже если вы используете МенеджерЗаписи, поскольку уникальный ключ (Период + Измерения) будет идентичным.
  3. Как обеспечить уникальность:
    • Если ОписаниеСообщения, ДействиеИсточник и ТипСообщения (измерения вашего регистра) всегда уникальны для каждой новой записи, то это уже обеспечивает уникальность ключа, и дублирование по Периоду менее вероятно приведет к перезаписи.
    • Если же измерения могут повторяться, а вам нужно гарантировать запись всех событий, даже происходящих в одну и ту же миллисекунду, то может потребоваться добавить дополнительное измерение, которое будет обеспечивать уникальность (например, порядковый номер записи в течение одной миллисекунды, генерируемый GUID или другое уникальное поле).

Структура регистра сведений "ЖурналОбменаССайтом"

Для полноты картины рассмотрим структуру регистра, который использовал автор, поскольку она влияет на формирование уникального ключа записи:

  1. Периодичность: В пределах секунды.
  2. Измерения:
    • ОписаниеСообщения (Строка 250)
    • ДействиеИсточник (Строка 250)
    • ТипСообщения (Перечисление: Ошибка, Предупреждение)
  3. Ресурсы/Реквизиты: Отсутствуют.

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

Заключение

Мы подробно разобрали проблему перезаписи записей в периодическом регистре сведений. Основная причина заключалась в некорректном использовании объекта НаборЗаписей без установки отбора, что приводило к полной очистке регистра при каждом вызове метода Записать().

Правильным и рекомендуемым решением для добавления одной записи в регистр сведений является использование объекта РегистрСведенийМенеджерЗаписи. Это обеспечивает простоту, эффективность и корректную обработку уникальности записей.

Помните о назначении каждого объекта (МенеджерЗаписи для одной записи, НаборЗаписей для коллекций) и об особенностях работы периодических регистров сведений. Эти знания помогут вам избегать подобных ошибок и эффективно работать с данными в 1С:Предприятии.

← На главную