Почему не фильтруются строки во вложенной табличной части запроса 1С при использовании условия НЕ В?

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

При написании запросов к документам и их табличным частям разработчики нередко пытаются получить документ вместе с вложенной табличной частью через синтаксис Документ.ТабличнаяЧасть.(...) и одновременно отфильтровать строки этой табличной части в секции ГДЕ. Однако на практике такое условие либо не исключает строки вовсе, либо отсекает документ целиком. Разберем по шагам, почему так происходит и как правильно решить эту задачу.

Анализ проблемы и механизма работы вложенных таблиц

Рассмотрим пример запроса, в котором возникает ошибка:


ВЫБРАТЬ
    ЗаказКлиента.Ссылка КАК Ссылка,
    ЗаказКлиента.Товары.(
        Номенклатура КАК Номенклатура
    )
ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента
ГДЕ
    ЗаказКлиента.Ссылка = &Ссылка
    И НЕ ЗаказКлиента.Товары.Номенклатура В
            (ВЫБРАТЬ
                ПеремещениеТоваровТовары.Номенклатура
            ИЗ
                Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
            ГДЕ
                ПеремещениеТоваровТовары.Ссылка.ДокументОснование = &СсылкаПеремещение)

Выясним причину такого поведения платформы. В языке запросов 1С обращение к полям табличной части через точку от основного объекта (ЗаказКлиента.Товары.Номенклатура) в секции ГДЕ транслируется на уровень СУБД как проверка на существование строк через предикат EXISTS для самого документа.

Секция ГДЕ определяет исключительно то, попадет ли ссылка на документ в результирующую выборку, но она не способна модифицировать состав строк внутри конструкции ЗаказКлиента.Товары.(...). Если в документе есть хотя бы одна позиция номенклатуры, удовлетворяющая условию (то есть не входящая в список исключения), условие для документа считается истинным. В результате платформа выбирает данный документ и возвращает его вложенную таблицу целиком со всеми исходными строками.

Кроме того, конструкция НЕ ... В (ВЫБРАТЬ ...) в базах данных несет потенциальный риск: если подзапрос вернет хотя бы одно значение NULL, условие НЕ В вернет неопределенный результат, и документ вообще не попадет в выборку.

Решение 1. Обращение напрямую к табличной части документа

Проанализируем стандартный и наиболее эффективный способ выборки. Если требуется получить отфильтрованный список строк документа, необходимо обращаться напрямую к реальной таблице табличной части — Документ.ЗаказКлиента.Товары, а не к шапке документа с вложенной таблицей.

Посмотрим на пример правильного построения запроса:


ВЫБРАТЬ
    ЗаказКлиентаТовары.Ссылка КАК Ссылка,
    ЗаказКлиентаТовары.Номенклатура КАК Номенклатура
ИЗ
    Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
ГДЕ
    ЗаказКлиентаТовары.Ссылка = &Ссылка
    И НЕ ЗаказКлиентаТовары.Номенклатура В
            (ВЫБРАТЬ
                ПеремещениеТоваровТовары.Номенклатура
            ИЗ
                Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
            ГДЕ
                ПеремещениеТоваровТовары.Ссылка.ДокументОснование = &СсылкаПеремещение)

В этом случае каждая запись табличной части обрабатывается как отдельная строка плоской таблицы. Фильтр в условии ГДЕ применяется к каждой строке персонально, отсекая только те позиции номенклатуры, которые присутствуют в документе перемещения.

Решение 2. Использование временных таблиц и левого соединения (Anti-Join)

Рассмотрим более производительный и отказоустойчивый вариант, рекомендованный стандартами разработки 1С. Использование подзапросов внутри НЕ В может приводить к неоптимальным планам выполнения запросов в СУБД. Рекомендуется заменить конструкцию НЕ В на ЛЕВОЕ СОЕДИНЕНИЕ с проверкой на ЕСТЬ NULL через временную таблицу.

Разберем по шагам реализацию пакетного запроса:

  1. Выбираем список номенклатуры для исключения и помещаем его во временную таблицу с обязательным индексированием поля связи.
  2. Выбираем строки целевого документа, соединяя их с временной таблицей через ЛЕВОЕ СОЕДИНЕНИЕ.
  3. В секции ГДЕ оставляем только те записи, для которых не нашлось соответствия в таблице исключений (ЕСТЬ NULL).

Посмотрим на текст оптимизированного запроса:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПеремещениеТоваровТовары.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ИсключаемаяНоменклатура
ИЗ
    Документ.ПеремещениеТоваров.Товары КАК ПеремещениеТоваровТовары
ГДЕ
    ПеремещениеТоваровТовары.Ссылка.ДокументОснование = &СсылкаПеремещение

ИНДЕКСИРОВАТЬ ПО
    Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ЗаказКлиентаТовары.Ссылка КАК Ссылка,
    ЗаказКлиентаТовары.Номенклатура КАК Номенклатура
ИЗ
    Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИсключаемаяНоменклатура КАК ВТ_ИсключаемаяНоменклатура
        ПО ЗаказКлиентаТовары.Номенклатура = ВТ_ИсключаемаяНоменклатура.Номенклатура
ГДЕ
    ЗаказКлиентаТовары.Ссылка = &Ссылка
    И ВТ_ИсключаемаяНоменклатура.Номенклатура ЕСТЬ NULL

Такой подход обеспечивает корректную построчную фильтрацию данных, гарантирует высокую скорость выполнения на больших объемах документов и исключает логические ошибки при наличии неопределенных значений.

← На главную