Работа с регистрами сведений — одна из самых частых задач при разработке на платформе 1С:Предприятие. Однако начинающие разработчики часто сталкиваются с ситуациями, когда данные либо записываются некорректно (дублируются), либо полностью затирают существующую информацию. В рамках данной статьи мы подробно разберем механизмы записи в регистр, проанализируем типичные ошибки и выясним, как управлять уникальностью записей.
Для записи данных из внешней обработки чаще всего используется объект РегистрСведенийНаборЗаписей. Основная особенность этого объекта заключается в том, что он работает не с одной строкой, а с целой выборкой, ограниченной установленными фильтрами (отборами). Если мы не установим отбор, система может попытаться перезаписать весь регистр целиком, что приведет к потере данных или ошибкам производительности.
Рассмотрим ситуацию, когда нам нужно обновить данные по конкретному пациенту. Проанализируем программный код, который позволяет корректно прочитать существующую запись и изменить её, либо создать новую, если данных еще нет:
НаборЗаписей = РегистрыСведений.ОбращенияЗаписи.СоздатьНаборЗаписей();
// Установка отборов по всем ключевым измерениям
НаборЗаписей.Отбор.Пациент.Установить(Пациент);
НаборЗаписей.Отбор.ДатаПервичногоПосещения.Установить(ДатаПервичногоПосещения);
НаборЗаписей.Отбор.УникальныйИдентификаторПоказателейЗдоровья.Установить(УникальныйИдентификаторПоказателейЗдоровья);
// Чтение текущего состояния регистра по данным фильтрам
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() = 0 Тогда
// Если записей нет, добавляем новую строку в набор
Запись = НаборЗаписей.Добавить();
Иначе
// Если запись найдена, работаем с первой строкой набора
Запись = НаборЗаписей[0];
КонецЕсли;
// Заполнение ресурсов и реквизитов
Запись.Пациент = Пациент;
Запись.ДатаПервичногоПосещения = ДатаПервичногоПосещения;
Запись.УникальныйИдентификаторПоказателейЗдоровья = УникальныйИдентификаторПоказателейЗдоровья;
Запись.ПоказательАртериальногоДавления = 120; // Пример ресурса
// Фиксация изменений в базе данных
НаборЗаписей.Записать();
Важный нюанс: метод Записать() по умолчанию принимает параметр Замещать = Истина. Это означает, что всё содержимое регистра, подходящее под установленный отбор, будет удалено и заменено содержимым вашего объекта НаборЗаписей. Если вы вызвали Прочитать(), изменили строку и записали — данные обновятся. Если вы НЕ вызвали Прочитать(), а просто добавили новую строку в пустой набор и записали его с установленным отбором — старая запись в базе будет удалена, а новая создана.
Если логика вашей обработки подразумевает изменение строго одной конкретной записи, то использование НабораЗаписей может быть избыточным. Выясним, как упростить код с помощью объекта МенеджерЗаписи. Этот объект работает аналогично записи справочника или документа.
Посмотрим на пример работы с менеджером записи:
МенеджерЗаписи = РегистрыСведений.ОбращенияЗаписи.СоздатьМенеджерЗаписи();
// Прямое заполнение измерений для поиска
МенеджерЗаписи.Пациент = Пациент;
МенеджерЗаписи.ДатаПервичногоПосещения = ДатаПервичногоПосещения;
МенеджерЗаписи.УникальныйИдентификаторПоказателейЗдоровья = УникальныйИдентификаторПоказателейЗдоровья;
// Попытка чтения записи из базы
МенеджерЗаписи.Прочитать();
Если Не МенеджерЗаписи.Выбран() Тогда
// Если записи не существует, значения измерений уже заполнены выше
// Можно дозаполнить ресурсы
КонецЕсли;
МенеджерЗаписи.ПоказательАртериальногоДавления = 130;
МенеджерЗаписи.Записать();
Преимущество этого метода в его наглядности и быстродействии при работе с единичными данными. Однако, если вам нужно записать 1000 строк за один раз, МенеджерЗаписи будет работать медленнее, так как он обращается к базе данных при каждой итерации Записать().
Часто возникает вопрос: почему одна запись записывается несколько раз? Проанализируем структуру регистра. В 1С уникальность записи определяется комбинацией значений его Измерений. Если в вашем регистре три измерения (Пациент, Дата, Идентификатор), то система не позволит создать две строки, у которых все три поля совпадают.
Если вы получаете дубли, проверьте следующие моменты:
Добавить() в цикле, система будет добавлять новые строки в набор, не видя существующих по другим измерениям.УникальныйИдентификаторПоказателейЗдоровья) получает новое значение при каждом запуске обработки, то для системы это всегда новая, уникальная запись.Период. Даже если все измерения совпадают, но разница в дате/времени составляет хотя бы одну секунду (при соответствующей настройке), это будут разные записи.Рассмотрим ситуацию из сообщения №7: пользователю нужно записать одинаковые данные дважды (например, два посещения в один день). В стандартном регистре сведений это невозможно без изменения его структуры. Чтобы реализовать такую возможность, выполним одно из следующих действий:
НомерСтроки (тип Число) или ИдентификаторЗаписи (тип УникальныйИдентификатор). При каждой новой записи генерируйте новое значение (Новый УникальныйИдентификатор()), и тогда записи не будут затирать друг друга.Проанализируем ситуацию, когда запись производится в высоконагруженной системе. Если ваша обработка записывает данные в цикле, крайне важно обеспечить целостность данных. Для этого используем транзакции. Рассмотрим пример:
НачатьТранзакцию();
Попытка
Набор = РегистрыСведений.ОбращенияЗаписи.СоздатьНаборЗаписей();
// ... логика заполнения набора ...
Набор.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ЗаписатьЛогОшибок(ОписаниеОшибки());
Сообщить("Ошибка при записи в регистр!");
КонецПопытки;
Использование транзакции гарантирует, что если при записи произойдет сбой (например, отключится питание или возникнет ошибка уникальности), база данных вернется в исходное состояние, и у вас не останется «битых» или частичных данных.
Также стоит помнить о блокировках данных. При вызове метода Записать() система автоматически устанавливает исключительную блокировку на записи, соответствующие отбору. Если другой пользователь в этот момент пытается прочитать эти же данные, он может столкнуться с ожиданием. Чтобы минимизировать конфликты, старайтесь устанавливать максимально точные отборы по измерениям.
Иногда требуется не обновить, а полностью «перезалить» данные по определенному критерию. Разберем, как сделать это правильно:
Набор.Записать(Истина). Это удалит все старые записи по этому пациенту.Записать().Такой подход гарантирует, что в регистре не останется «хвостов» от старых данных, которые могли иметь другие значения дополнительных измерений.