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

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

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

Метод 1. Использование Менеджера записи (самый простой способ)

Для работы с одной конкретной записью в 1С предусмотрен специальный объект — РегистрСведенийМенеджерЗаписи. Этот объект позволяет оперировать записью как отдельной сущностью. Рассмотрим, как с его помощью проверить существование записи.

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


МенеджерЗаписи = РегистрыСведений.МойРегистр.СоздатьМенеджерЗаписи();
// Заполняем измерения, по которым ищем запись
МенеджерЗаписи.Измерение1 = Значение1;
МенеджерЗаписи.Измерение2 = Значение2;

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

Если МенеджерЗаписи.Выбран() Тогда
    // Запись существует, здесь можно изменить ресурсы
    МенеджерЗаписи.Ресурс1 = НовоеЗначение;
Иначе
    // Записи нет, заполняем все необходимые поля для создания
    МенеджерЗаписи.Измерение1 = Значение1;
    МенеджерЗаписи.Измерение2 = Значение2;
    МенеджерЗаписи.Ресурс1 = НовоеЗначение;
КонецЕсли;

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

Важное замечание: Менеджер записи при чтении извлекает из базы все поля (ресурсы и реквизиты). Если ваша цель — просто изменить один ресурс, не зная значений остальных, этот метод является наиболее подходящим, так как он сохранит старые значения тех полей, которые вы не меняли.

Метод 2. Запись «вслепую» (без предварительного чтения)

Многие начинающие разработчики полагают, что перед записью обязательно нужно делать проверку. Однако платформа 1С спроектирована так, что метод Записать() у менеджера записи по умолчанию работает в режиме замещения. Проанализируем параметры метода Записать(Замещать):

  1. Если параметр Замещать равен Истина (значение по умолчанию), то система проверяет наличие записи по ключевым полям. Если запись есть — она обновляется, если нет — создается.
  2. Если параметр равен Ложь, система попытается именно добавить новую запись. Если такая запись уже существует, возникнет ошибка выполнения (исключение).

Таким образом, если вы точно знаете значения всех ресурсов, которые должны быть в итоге, проверку можно пропустить:


МенеджерЗаписи = РегистрыСведений.МойРегистр.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Измерение1 = Значение1;
МенеджерЗаписи.Ресурс1 = ЗначениеРесурса;
// Запись будет либо создана, либо перезаписана целиком
МенеджерЗаписи.Записать(Истина); 

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

Метод 3. Работа с Набором записей

Использование объекта РегистрСведенийНаборЗаписей является более универсальным и «профессиональным» подходом, особенно когда нужно работать с группой записей или когда регистр подчинен регистратору. Рассмотрим ситуацию с независимым регистром.

При использовании набора записей крайне важно правильно установить Отбор. Если вы запишете набор без отбора, платформа очистит весь регистр! Выясним, как безопасно проверить и изменить запись через набор:


Набор = РегистрыСведений.МойРегистр.СоздатьНаборЗаписей();
// Обязательно устанавливаем отбор по ключевым измерениям
Набор.Отбор.Измерение1.Установить(Значение1);

Набор.Прочитать();

Если Набор.Количество() = 0 Тогда
    // Записи нет, добавляем новую
    НоваяЗапись = Набор.Добавить();
    НоваяЗапись.Измерение1 = Значение1;
    НоваяЗапись.Ресурс1 = ЗначениеРесурса;
Иначе
    // Запись найдена (в наборе будет одна строка, если отбор по всем измерениям)
    ТекущаяЗапись = Набор[0];
    ТекущаяЗапись.Ресурс1 = ЗначениеРесурса;
КонецЕсли;

Набор.Записать();

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

Метод 4. Проверка существования через Запрос

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

Посмотрим на пример кода:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |   1 КАК Поле
    |ИЗ
    |   РегистрСведений.МойРегистр КАК МойРегистр
    |ГДЕ
    |   МойРегистр.Измерение1 = &Значение1";

Запрос.УстановитьПараметр("Значение1", Значение1);

РезультатЗапроса = Запрос.Выполнить();

Если РезультатЗапроса.Пустой() Тогда
    // Записи нет
Иначе
    // Запись точно есть
КонецЕсли;

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

Почему «читать перед записью» — это не всегда ошибка?

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

Если вы просто создадите менеджер записи, укажете измерения и новый комментарий, а затем вызовете Записать(), то Количество и Сумма станут равны нулю. Платформа не знает, что там было раньше, она просто записывает то, что есть в объекте. Именно поэтому в таких случаях чтение перед записью обязательно. Мы читаем состояние записи из БД в память, меняем только нужный реквизит и записываем обратно весь «пакет» данных.

Производительность и блокировки данных

Проанализируем ситуацию в многопользовательской среде. Когда два пользователя одновременно проверяют наличие одной и той же записи (схема «Прочитать -> Проверить -> Записать»), может возникнуть ситуация гонки (race condition). Оба увидят, что записи нет, и оба попытаются ее создать, что приведет к ошибке блокировки или дублированию логики.

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

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

Выясним, какой метод выбрать в зависимости от ситуации:

  1. Нужно быстро перезаписать все данные? Используйте МенеджерЗаписи с установкой измерений и ресурсов и вызывайте Записать(Истина) без предварительного чтения.
  2. Нужно изменить только одно поле, сохранив остальные? Обязательно используйте МенеджерЗаписи.Прочитать() или НаборЗаписей.Прочитать(), модифицируйте поле и записывайте.
  3. Нужно просто проверить наличие для логики алгоритма? Используйте Запрос к таблице регистра, это самый легкий способ для системы.
  4. Работаете с периодическим регистром? Посмотрите в сторону виртуальной таблицы СрезПоследних, часто проверка фактического наличия записи на дату менее эффективна, чем получение актуального значения ресурса.

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

← На главную