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

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

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

Отказ от циклов в пользу пакетной обработки

Главное правило оптимизации в 1С: никогда не делайте запрос внутри цикла, если можно получить те же данные за один раз. В нашем случае мы работаем со списком сотрудников, который уже есть у нас в табличной части. Вместо того чтобы спрашивать базу данных для каждого сотрудника по отдельности, мы соберем их всех в один массив или список и отправим один запрос.

Способ 1: Использование массива значений в запросе

Это классический и самый понятный метод. Суть его заключается в том, чтобы собрать всех сотрудников в одну коллекцию и передать её в условие ГДЕ нашего запроса.

Разберем шаги реализации:

  1. Выгружаем колонку с сотрудниками из табличной части в массив с помощью метода ВыгрузитьКолонку.
  2. Передаем этот массив в запрос как параметр.
  3. Используем оператор В в секции ГДЕ.

Посмотрим, как это выглядит в коде:


// Предположим, что табличная часть называется "Сотрудники"
МассивСотрудников = Объект.Сотрудники.ВыгрузитьКолонку("Сотрудник");

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    РегистрСведений.СотрудникиКатегории.Сотрудник КАК Сотрудник,
    |    РегистрСведений.СотрудникиКатегории.Категория КАК Категория
    |ИЗ
    |    РегистрСведений.СотрудникиКатегории КАК РегистрСведений
    |ГДЕ
    |    РегистрСведений.Сотрудник В (&МассивСотрудников)";

Запрос.УстановитьПараметр("МассивСотрудников", МассивСотрудников);
РезультатЗапроса = Запрос.Выполнить();

Теперь в РезультатЗапроса у нас содержатся данные только по тем сотрудникам, которые есть в нашем документе. Это значительно быстрее, чем 100 запросов для 100 сотрудников.

Способ 2: Левое соединение с табличной частью

Если проверка выполняется непосредственно в процедуре ОбработкаПроведения, мы можем поступить еще изящнее. Мы можем объединить данные нашего документа с данными регистра прямо в одном запросе.

В чем преимущество такого подхода? Мы получаем всю таблицу документа и сразу видим «пустые» места. Если запись в регистре есть, мы увидим данные, если записи нет — в поле регистра будет значение NULL.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЧ.Сотрудник,
    |    Регистр.Категория
    |ИЗ
    |    Документ.НашДокумент.Сотрудники КАК ТЧ
    |        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СотрудникиКатегории КАК Регистр
    |        ПО ТЧ.Сотрудник = Регистр.Сотрудник
    |ГДЕ
    |    ТЧ.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", Ссылка);
Результат = Запрос.Выполнить().Выбрать();

В этом примере ЛЕВОЕ СОЕДИНЕНИЕ позволяет нам увидеть всех сотрудников из табличной части. Те, у кого нет категории, будут иметь NULL в поле Категория, что позволит нам легко отфильтровать или обработать такие ситуации.

Чего делать не стоит: Менеджер записи

Часто встречается ошибка, когда разработчики пытаются использовать РегистрыСведений.ИмяРегистра.СоздатьМенеджерЗаписи() в цикле. Это антипаттерн. МенеджерЗаписи предназначен для работы с одной конкретной записью (чтение, запись или удаление). При попытке использовать его для массовой проверки система будет каждый раз обращаться к базе данных, что приведет к «тормозам» документа при проведении.

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

Подводя итог нашей совместной работы, запомним основные тезисы:

Применяя эти простые подходы, вы сделаете ваш код не только быстрым, но и профессиональным, что существенно упростит поддержку конфигурации в будущем!

← На главную