Как выбрать элементы справочника, в табличных частях которых отсутствует заданное значение

Программист IT и автоматизация бизнеса
← На главную

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

Почему классическое условие «НЕ РАВНО» не работает

Рассмотрим ситуацию, которую часто допускают начинающие разработчики. Допустим, мы пишем условие ГДЕ ТЧ.Товар <> &Товар. Если в табличной части элемента справочника есть две строки — «Товар1» и «Товар2», а мы ищем отсутствие «Товар1», то строка с «Товар2» успешно пройдет фильтр <>. В результате запрос вернет нам этот элемент справочника, несмотря на то что «Товар1» в нем присутствует в другой строке. Нам же нужно убедиться, что ни одна строка не содержит искомое значение.

Проанализируем правильные подходы к решению этой задачи.

Способ 1. Использование ЛЕВОЕ СОЕДИНЕНИЕ и проверки на NULL

Это наиболее эффективный и читаемый способ. Его суть заключается в том, что мы пытаемся присоединить табличную часть только по тем строкам, которые содержат «запрещенное» значение. Если таких строк нет, соединение вернет NULL для полей правой таблицы. Именно эти объекты нам и нужны.

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

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

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


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

Выясним причину, почему это работает: когда мы ставим условие СпрПартнеры.Партнер = &Партнер в условии связи, СУБД пытается найти в табличной части только те строки, которые совпадают с параметром. Если таких строк нет, вся «правая часть» для данного элемента справочника будет заполнена значениями NULL. Условие ЕСТЬ NULL в конце запроса отсекает все объекты, где было найдено хотя бы одно совпадение.

Способ 2. Использование временных таблиц и объединений (Оптимизированный)

Если табличных частей много, а объем данных в базе велик, использование множества ЛЕВОЕ СОЕДИНЕНИЕ может привести к декартову произведению строк и замедлению работы. В этом случае лучше сначала собрать все «плохие» ссылки (те, которые содержат исключаемые значения) в одну временную таблицу.

Проанализируем этот алгоритм:

  1. Создаем временную таблицу ВТ_ИсключаемыеСсылки.
  2. Используем ОБЪЕДИНИТЬ ВСЕ, чтобы собрать ссылки из всех ТЧ, где есть совпадение с параметрами.
  3. В основном запросе выбираем данные, исключая те, что попали во временную таблицу.

Пример реализации:


// Шаг 1: Собираем все ссылки, которые нам НЕ подходят
ВЫБРАТЬ СпрПартнеры.Ссылка ПОМЕСТИТЬ ВТ_ИсключаемыеСсылки ИЗ Справочник.Справочник1.Партнеры КАК СпрПартнеры ГДЕ СпрПартнеры.Партнер = &Партнер
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ СпрТовары.Ссылка ИЗ Справочник.Справочник1.Товары КАК СпрТовары ГДЕ СпрТовары.Номенклатура В (&СписокНоменклатуры)
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ СпрСклады.Ссылка ИЗ Справочник.Справочник1.Склады КАК СпрСклады ГДЕ СпрСклады.Склад = &Склад;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Выбираем основные данные, исключая найденные ссылки
ВЫБРАТЬ
    Спр.Ссылка
ИЗ
    Справочник.Справочник1 КАК Спр
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИсключаемыеСсылки КАК Исключения
    ПО Спр.Ссылка = Исключения.Ссылка
ГДЕ
    Спр.ДатаНачала = &ДатаДок
    И Исключения.Ссылка ЕСТЬ NULL

Этот метод считается более профессиональным для высоконагруженных систем. Использование ОБЪЕДИНИТЬ ВСЕ работает быстрее, чем множественные соединения к разным физическим таблицам табличных частей в одном пакете.

Важные нюансы и советы по производительности

При реализации подобных запросов следует учитывать несколько критически важных моментов:

Таким образом, мы рассмотрели два надежных способа решения задачи. Выбор между ними зависит от сложности вашей структуры данных: для простых задач достаточно ЛЕВОЕ СОЕДИНЕНИЕ, для сложных и объемных — лучше использовать временные таблицы с объединением результатов.

← На главную