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