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