При разработке в среде 1С:Предприятие программисты часто сталкиваются с ситуацией, когда при записи в регистр сведений данные не добавляются новыми строками, а перезаписывают существующие. В худшем случае из регистра вообще пропадают все ранее накопленные данные, кроме одной-единственной последней записи. В этой статье мы подробно разберем, почему так происходит, какие архитектурные особенности платформы 1С к этому приводят, и как правильно реализовать бесконфликтную запись данных.
Рассмотрим типовую ошибку, представленную в коде автора темы. Для ведения лога обращений к Active Directory (AD) используется периодический регистр сведений. При выполнении процедуры логирования разработчик использует следующий код:
НаборЗаписей = РегистрыСведений.ИсторияОбращенийКAD.СоздатьНаборЗаписей;
НоваяЗапись = НаборЗаписей.Добавить;
НоваяЗапись.Период = ТекущаяДата;
НоваяЗапись.ТекстКоманды = КомандаАД;
// Заполнение остальных реквизитов...
НаборЗаписей.Записать;
В данном фрагменте кода кроются сразу две критические ошибки, которые приводят к затиранию данных. Проанализируем их подробнее.
Ошибка №1: Запись пустого набора (отсутствие отборов)
Объект НаборЗаписей предназначен для работы с некоторой выборкой (порцией) данных регистра. Когда мы вызываем метод Записать(), платформа по умолчанию выполняет запись с параметром замещения: Записать(Истина). Это означает, что перед физической записью новых строк СУБД должна удалить из таблицы регистра все старые строки, соответствующие установленным отборам набора записей.
Если в коде **не установлены отборы** по измерениям (или по периоду для периодического регистра), платформа интерпретирует это как команду: «Очистить абсолютно весь регистр сведений и записать поверх только те строки, которые сейчас находятся в коллекции набора». Поскольку в коллекцию была добавлена всего одна новая строка, при каждом вызове процедуры весь регистр полностью очищается, и в нем сохраняется только одна (последняя) запись. Это классическая ошибка, способная привести к полной потере данных в рабочей базе.
Ошибка №2: Заблуждение об автоматическом заполнении UUID
В периодическом регистре сведений автора темы единственным измерением является КлючУникальности с типом данных УникальныйИдентификатор. Автор предположил, что платформа 1С автоматически генерирует новое уникальное значение для этого поля при добавлении записи, аналогично тому, как это происходит со ссылками справочников или документов.
Это не так. Платформа 1С никогда автоматически не генерирует значения для измерений регистров сведений, даже если у них установлен тип УникальныйИдентификатор. Если мы не заполняем это поле принудительно в коде, система записывает туда пустое значение UUID: 00000000-0000-0000-0000-000000000000.
В результате, если в пределах одной секунды (минимальная дискретность периодического регистра) происходит повторная запись, ключевые поля новой записи (Период + КлючУникальности) полностью совпадают с ключевыми полями уже существующей записи. Система заменяет старую запись новой, поскольку уникальность в регистре сведений определяется строго по совокупности измерений и периода.
---
Если перед нами стоит задача логирования (когда мы записываем строго по одной строке за раз), лучшим решением с точки зрения безопасности и лаконичности кода является использование объекта МенеджерЗаписи.
Давайте посмотрим, как переписать процедуру логирования с использованием этого объекта:
Процедура ЗаписатьИнформациюОбОбращенииКАД(КомандаАД, Успех, Ошибка)
мЛогироватьОбращениеКAD = Константы.ЛогироватьОбращениеКAD.Получить();
Если Не мЛогироватьОбращениеКAD Тогда
Возврат;
КонецЕсли;
// Создаем менеджер записи для работы с одной строкой
МенеджерЗаписи = РегистрыСведений.ИсторияОбращенийКAD.СоздатьМенеджерЗаписи();
// Заполняем ключевые поля (Период и Измерения)
МенеджерЗаписи.Период = ТекущаяДата();
МенеджерЗаписи.КлючУникальности = Новый УникальныйИдентификатор(); // Генерируем UUID вручную!
// Заполняем реквизиты и ресурсы
МенеджерЗаписи.ТекстКоманды = КомандаАД;
МенеджерЗаписи.ДоменКонтролер = Справочники.ВидыРесурсов.АД;
МенеджерЗаписи.Статус = Успех;
МенеджерЗаписи.ОписаниеОшибки = Ошибка;
// Записываем данные в базу. Метод Записать() у менеджера записи безопасен.
МенеджерЗаписи.Записать(Истина);
КонецПроцедуры
Почему этот подход безопасен?
Объект МенеджерЗаписи оперирует только одной строкой таблицы. Он физически не способен очистить весь регистр сведений. Метод Записать(Истина) в данном случае выполнит поиск записи по ключевым полям (в нашем примере это уникальное сочетание Период + КлючУникальности). Если запись с таким ключом уже есть — она обновится, если нет — создастся новая. А поскольку мы генерируем уникальный идентификатор программно для каждого обращения, перезапись исключена.
---
Бывают ситуации, когда логика системы требует использования именно объекта НаборЗаписей. Например, при групповой обработке данных или при необходимости записать массив строк одной транзакцией. В этом случае мы обязаны строго соблюдать правила установки отборов и генерации ключевых полей.
Рассмотрим правильный пример записи с помощью НаборЗаписей:
Процедура ЗаписатьЛогНабором(КомандаАД, Успех, Ошибка)
НаборЗаписей = РегистрыСведений.ИсторияОбращенийКAD.СоздатьНаборЗаписей();
// 1. Формируем новые уникальные значения ключевых полей
ДатаСеанса = ТекущаяДата();
НовыйКлюч = Новый УникальныйИдентификатор();
// 2. Строго устанавливаем отборы по ВСЕМ измерениям и периоду!
// Это гарантирует, что 1С очистит только ту область данных, которую мы сейчас пишем
НаборЗаписей.Отбор.Период.Установить(ДатаСеанса);
НаборЗаписей.Отбор.КлючУникальности.Установить(НовыйКлюч);
// 3. Добавляем запись в подготовленный набор
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Период = ДатаСеанса;
НоваяЗапись.КлючУникальности = НовыйКлюч;
НоваяЗапись.ТекстКоманды = КомандаАД;
НоваяЗапись.Статус = Успех;
НоваяЗапись.ОписаниеОшибки = Ошибка;
// 4. Записываем набор
НаборЗаписей.Записать(Истина);
КонецПроцедуры
При такой реализации СУБД выполнит запрос на удаление записей, где Период = ДатаСеанса и КлючУникальности = НовыйКлюч. Так как ключ только что сгенерирован и такой записи еще нет в базе, ничего удалено не будет, а наша новая строка успешно запишется в регистр.
Альтернативный вариант: запись без замещения
Если мы уверены, что отборы устанавливать не нужно, но хотим просто "дописать" строки в конец таблицы без очистки существующих данных, мы можем передать в метод Записать параметр Ложь:
НаборЗаписей.Записать(Ложь);
При использовании Записать(Ложь) платформа выполняет прямую вставку (INSERT) строк в СУБД. Однако помните: если в наборе окажется запись с ключом, который уже существует в базе данных, транзакция прервется с ошибкой уникальности транзакции. Поэтому ручная генерация Новый УникальныйИдентификатор() для измерения остается строго обязательной.
---
Выясним, в каких сценариях эффективнее использовать тот или иной объект доступа к регистру сведений. Для этого проанализируем их ключевые особенности:
МенеджерЗаписи подходит исключительно для единичных операций (запись лога, изменение одной настройки пользователя). Он не предназначен для массовой работы.
НаборЗаписей эффективен для пакетной записи данных. Если вам нужно записать 1000 строк лога, накопленных в буфере, использование менеджера записи в цикле приведет к критическому снижению производительности, так как на каждую строку СУБД будет выполнять отдельную транзакцию. Набор записей запишет весь пакет данных за одно обращение к базе данных.
МенеджерЗаписи максимально безопасен. Ошибка разработчика при его использовании никогда не сотрет данные соседних строк регистра.
НаборЗаписей требует высокой концентрации внимания. Запись набора без установленных отборов с параметром замещения по умолчанию уничтожает данные в регистре.
При записи набора система блокирует диапазон данных по установленным отборам. При записи менеджера блокируется только одна конкретная строка (запись), что снижает вероятность возникновения конфликтов блокировок в многопользовательском режиме.
Подводя итог, можно дать простое правило: для добавления единичных записей (особенно в транзакциях логирования, проведения документов по дополнительным регистрам "в один клик") используем безопасный МенеджерЗаписи. Для групповой перезаписи данных, первоначального заполнения или загрузки больших пакетов информации из внешних источников используем оптимизированный НаборЗаписей с обязательным контролем отборов и ручным заполнением ключевых уникальных полей.