При работе с запросами в 1С программисты часто сталкиваются с задачей «инвертированного поиска». Типичный пример: нужно найти все документы или элементы справочников, в которых ни в одной строке нескольких табличных частей не встречается определенное значение (номенклатура, склад, партнер и т.д.). Обычное условие «не равно» (<>) здесь не работает, так как оно фильтрует конкретные строки, а не весь объект целиком. В этой статье мы разберем, как правильно составить такой запрос, избежав логических ловушек и проблем с производительностью.
Рассмотрим ситуацию, которую часто допускают начинающие разработчики. Допустим, мы пишем условие ГДЕ ТЧ.Товар <> &Товар. Если в табличной части элемента справочника есть две строки — «Товар1» и «Товар2», а мы ищем отсутствие «Товар1», то строка с «Товар2» успешно пройдет фильтр <>. В результате запрос вернет нам этот элемент справочника, несмотря на то что «Товар1» в нем присутствует в другой строке. Нам же нужно убедиться, что ни одна строка не содержит искомое значение.
Проанализируем правильные подходы к решению этой задачи.
Это наиболее эффективный и читаемый способ. Его суть заключается в том, что мы пытаемся присоединить табличную часть только по тем строкам, которые содержат «запрещенное» значение. Если таких строк нет, соединение вернет NULL для полей правой таблицы. Именно эти объекты нам и нужны.
Разберем по шагам создание такого запроса:
ЛЕВОЕ СОЕДИНЕНИЕ.ПО), а не в ГДЕ.ГДЕ проверяем, что ссылка из присоединенной таблицы ЕСТЬ NULL.Посмотрим на пример кода:
ВЫБРАТЬ
Справочник1.Ссылка КАК Ссылка
ИЗ
Справочник.Справочник1 КАК Справочник1
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник1.Партнеры КАК СпрПартнеры
ПО Справочник1.Ссылка = СпрПартнеры.Ссылка
И СпрПартнеры.Партнер = &Партнер
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник1.Товары КАК СпрТовары
ПО Справочник1.Ссылка = СпрТовары.Ссылка
И СпрТовары.Номенклатура В (&СписокНоменклатуры)
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник1.Склады КАК СпрСклады
ПО Справочник1.Ссылка = СпрСклады.Ссылка
И СпрСклады.Склад = &Склад
ГДЕ
Справочник1.ДатаНачала = &ДатаДок
И СпрПартнеры.Ссылка ЕСТЬ NULL
И СпрТовары.Ссылка ЕСТЬ NULL
И СпрСклады.Ссылка ЕСТЬ NULL
Выясним причину, почему это работает: когда мы ставим условие СпрПартнеры.Партнер = &Партнер в условии связи, СУБД пытается найти в табличной части только те строки, которые совпадают с параметром. Если таких строк нет, вся «правая часть» для данного элемента справочника будет заполнена значениями NULL. Условие ЕСТЬ NULL в конце запроса отсекает все объекты, где было найдено хотя бы одно совпадение.
Если табличных частей много, а объем данных в базе велик, использование множества ЛЕВОЕ СОЕДИНЕНИЕ может привести к декартову произведению строк и замедлению работы. В этом случае лучше сначала собрать все «плохие» ссылки (те, которые содержат исключаемые значения) в одну временную таблицу.
Проанализируем этот алгоритм:
ВТ_ИсключаемыеСсылки.ОБЪЕДИНИТЬ ВСЕ, чтобы собрать ссылки из всех ТЧ, где есть совпадение с параметрами.Пример реализации:
// Шаг 1: Собираем все ссылки, которые нам НЕ подходят
ВЫБРАТЬ СпрПартнеры.Ссылка ПОМЕСТИТЬ ВТ_ИсключаемыеСсылки ИЗ Справочник.Справочник1.Партнеры КАК СпрПартнеры ГДЕ СпрПартнеры.Партнер = &Партнер
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ СпрТовары.Ссылка ИЗ Справочник.Справочник1.Товары КАК СпрТовары ГДЕ СпрТовары.Номенклатура В (&СписокНоменклатуры)
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ СпрСклады.Ссылка ИЗ Справочник.Справочник1.Склады КАК СпрСклады ГДЕ СпрСклады.Склад = &Склад;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Выбираем основные данные, исключая найденные ссылки
ВЫБРАТЬ
Спр.Ссылка
ИЗ
Справочник.Справочник1 КАК Спр
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИсключаемыеСсылки КАК Исключения
ПО Спр.Ссылка = Исключения.Ссылка
ГДЕ
Спр.ДатаНачала = &ДатаДок
И Исключения.Ссылка ЕСТЬ NULL
Этот метод считается более профессиональным для высоконагруженных систем. Использование ОБЪЕДИНИТЬ ВСЕ работает быстрее, чем множественные соединения к разным физическим таблицам табличных частей в одном пакете.
При реализации подобных запросов следует учитывать несколько критически важных моментов:
Номенклатура или Склад), имеют свойство «Индексировать». Без этого запрос будет выполнять полное сканирование таблиц, что недопустимо при большом количестве записей.СпрТовары.Номенклатура = &Номенклатура) в секцию ГДЕ, 1С (и SQL) автоматически преобразует ваше ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ. В итоге вы вообще не получите записей, у которых табличная часть пуста или не содержит искомого значения. Всегда держите фильтры правой таблицы в секции ПО.Таким образом, мы рассмотрели два надежных способа решения задачи. Выбор между ними зависит от сложности вашей структуры данных: для простых задач достаточно ЛЕВОЕ СОЕДИНЕНИЕ, для сложных и объемных — лучше использовать временные таблицы с объединением результатов.