Как проверить наличие реквизита у документа в запросе 1С

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

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

В этой статье мы подробно разберем несколько способов решения этой задачи — от самых простых до наиболее оптимизированных с точки зрения производительности SQL-сервера.

Метод 1: Использование конструкции ЕСТЬNULL

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

Рассмотрим пример реализации такого условия в секции ГДЕ или в выражении ВЫБОР:


ВЫБОР
    КОГДА ЕСТЬNULL(ТоварыНаСкладахОбороты.Регистратор.Партнер, НЕОПРЕДЕЛЕНО) В (&СписокЗначений)
        ТОГДА ЛОЖЬ
    ИНАЧЕ ИСТИНА
КОНЕЦ

Или в более лаконичном варианте:


ГДЕ НЕ ЕСТЬNULL(ТоварыНаСкладахОбороты.Регистратор.Партнер, НЕОПРЕДЕЛЕНО) В (&СписокЗначений)

Почему это работает: Если реквизита Партнер у документа нет, ЕСТЬNULL заменит его на НЕОПРЕДЕЛЕНО. Поскольку НЕОПРЕДЕЛЕНО обычно не входит в ваш &СписокЗначений (содержащий ссылки на справочник партнеров), условие отработает корректно для документов без реквизита. Однако стоит помнить, что этот метод заставляет систему выполнять множество автоматических LEFT JOIN ко всем таблицам документов, входящих в состав типа Регистратор.

Метод 2: Фильтрация по типам документов через параметры

Для повышения производительности мы можем заранее определить список типов документов, у которых интересующий нас реквизит точно существует. Выясним, как это сделать программно перед выполнением запроса:

  1. Проанализируем метаданные и соберем массив типов документов, содержащих нужный реквизит.
  2. Передадим этот массив в запрос как параметр &МассивТипов.
  3. В самом запросе сначала проверим тип регистратора.

Пример кода на встроенном языке:


МассивТипов = Новый Массив;
Для Каждого МетаДанное Из Метаданные.Документы Цикл
    Если МетаДанное.Реквизиты.Найти("Партнер") <> Неопределено Тогда
        МассивТипов.Добавить(Тип("ДокументСсылка." + МетаДанное.Имя));
    КонецЕсли;
КонецЦикла;
Запрос.УстановитьПараметр("МассивТипов", МассивТипов);

Теперь в запросе мы можем использовать безопасное условие:


ГДЕ ТИПЗНАЧЕНИЯ(ТоварыНаСкладахОбороты.Регистратор) В (&МассивТипов)
    И ТоварыНаСкладахОбороты.Регистратор.Партнер В (&СписокЗначений)

Это решение гораздо "чище", так как оно явно говорит планировщику запросов, к каким типам объектов мы обращаемся.

Метод 3: Оптимизация через ВЫРАЗИТЬ и ОБЪЕДИНИТЬ

Если ваша база данных содержит огромные объемы данных, обращение через точку к составному типу может значительно замедлить работу. В таких случаях разберем наиболее профессиональный подход — разделение запроса на части с использованием ВЫРАЗИТЬ.

Функция ВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг) позволяет платформе не гадать, к какой таблице обращаться, а строить прямой JOIN к таблице реализации. Если мы хотим проверить существование реквизита максимально быстро, мы можем построить запрос так:


ВЫБРАТЬ
    Т.Регистратор,
    Т.КоличествоОборот
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Обороты(...) КАК Т
ГДЕ
    (ВЫРАЗИТЬ(Т.Регистратор КАК Документ.РеализацияТоваровУслуг)).Партнер В (&СписокЗначений)
    ИЛИ (ВЫРАЗИТЬ(Т.Регистратор КАК Документ.ПоступлениеТоваровУслуг)).Партнер В (&СписокЗначений)
    // и так далее для всех нужных типов

Важный нюанс: если типов очень много, лучше использовать временные таблицы. Сначала выберем все данные из регистра во временную таблицу, а затем присоединим к ней только те типы документов, которые нам нужны, используя ВНУТРЕННЕЕ СОЕДИНЕНИЕ или ГДЕ ССЫЛКА.

Метод 4: Проверка типа через оператор ССЫЛКА

Если нам нужно обработать только конкретный набор документов, проанализируем ситуацию с использованием оператора ССЫЛКА. Это работает быстрее, чем ТИПЗНАЧЕНИЯ, так как транслируется в более простой SQL-код.


ВЫБОР
    КОГДА ТоварыНаСкладахОбороты.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
         ИЛИ ТоварыНаСкладахОбороты.Регистратор ССЫЛКА Документ.ВозвратТоваровОтКлиента
    ТОГДА ВЫБОР
            КОГДА ТоварыНаСкладахОбороты.Регистратор.Партнер В (&СписокЗначений) ТОГДА ЛОЖЬ
            ИНАЧЕ ИСТИНА
         КОНЕЦ
    ИНАЧЕ ИСТИНА // Для всех документов, где реквизита нет
КОНЕЦ

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

Подведем итоги и выберем лучшее решение

Рассмотрим, какой метод выбрать в зависимости от ваших задач:

  1. Для простых отчетов, которые запускаются редко: используйте ЕСТЬNULL(Регистратор.Партнер, ЗНАЧЕНИЕ(Справочник.Партнеры.ПустаяСсылка)). Это быстро пишется и легко читается.
  2. Для высоконагруженных систем: используйте предварительный сбор типов документов через метаданные и передачу массива в запрос. Это исключит лишние соединения таблиц на уровне SQL-сервера.
  3. Если типов регистраторов очень много (50+): старайтесь избегать обращений через точку в основном запросе. Используйте временные таблицы для предварительной фильтрации данных по типам.

Помните, что каждый "проход через точку" от поля с составным типом — это потенциальная точка снижения производительности. Проектируйте ваши запросы осознанно, учитывая особенности работы платформы 1С с метаданными.

← На главную