При проектировании запросов в системе «1С:Предприятие» разработчики регулярно сталкиваются с дилеммой: как эффективнее наложить фильтр по списку значений или временной таблице? Обычно выбирают между использованием оператора В с вложенным запросом и явным соединением таблиц (JOIN).
Кажется, что оба варианта решают одну и ту же задачу, но под капотом СУБД выполняет эти операции совершенно по-разному. Разберем детально физику процессов, разницу в логике работы и выясним, какой вариант и в каких сценариях будет работать быстрее.
Для начала проанализируем примеры запросов, которые часто вызывают споры среди разработчиков. Первый вариант использует конструкцию ГДЕ ... В (ВЫБРАТЬ ...):
ВЫБРАТЬ
Спр.Ссылка КАК Ссылка
ИЗ
Справочник.Справочник КАК Спр
ГДЕ
Спр.Реквизит В (ВЫБРАТЬ ТЗ.Ссылка ИЗ ВТ_ТЗ КАК ТЗ)
Второй вариант, предложенный в обсуждении, использует левое соединение с условием в секции ГДЕ:
ВЫБРАТЬ
Спр.Ссылка КАК Ссылка
ИЗ
ВТ_ТЗ КАК ТЗ
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник КАК Спр
ПО (1=1) // или без явного ПО
ГДЕ
ТЗ.Ссылка = Спр.Реквизит
Важное методическое замечание: Второй вариант написания запроса является грубой ошибкой проектирования. Наложение жесткого фильтра на правую таблицу в секции ГДЕ фактически превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Однако СУБД при этом тратит лишние ресурсы на построение плана, а оптимизатор может запутаться. Если нам нужно сопоставить данные, правильным решением будет использование явного внутреннего соединения с условием связи в секции ПО:
ВЫБРАТЬ
Спр.Ссылка КАК Ссылка
ИЗ
ВТ_ТЗ КАК ТЗ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Справочник КАК Спр
ПО ТЗ.Ссылка = Спр.Реквизит
Теперь сравним корректный вариант с использованием оператора В и классическое ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
Главное логическое различие между этими конструкциями заключается в обработке дублирующихся строк во временной таблице источника (ВТ_ТЗ).
РАЗЛИЧНЫЕ или группировку, что создаст дополнительную вычислительную нагрузку на процессор и оперативную память сервера СУБД.Таким образом, если во временной таблице гарантированно нет дубликатов (например, она предварительно сгруппирована), оба варианта вернут одинаковое количество строк. Если же уникальность не гарантирована, использование оператора В безопаснее и логически чище.
Выбор оптимального метода сильно зависит от размера временной таблицы, которая используется для фильтрации.
Маленький объем данных (до нескольких сотен строк):
При небольшом объеме временной таблицы оператор В работает очень быстро. СУБД часто преобразует такой запрос в план с использованием физического оператора *Nested Loops* (вложенные циклы) с поиском по индексу справочника. Производительность в данном случае будет практически идентичной или немного в пользу В.
Большой объем данных (тысячи и десятки тысяч строк):
При работе с большими наборами данных оптимизаторы современных СУБД (MS SQL Server, PostgreSQL) отдают предпочтение явному **Внутреннему соединению**. В этом случае СУБД проще построить стабильный план на основе эффективных физических операторов, таких как *Merge Join* (соединение слиянием) или *Hash Match* (хэш-соединение).
Если использовать оператор В (ВЫБРАТЬ...) на больших таблицах, оптимизатор СУБД может ошибиться в оценке кардинальности (количества строк) вложенного подзапроса. Ошибка в оценке приводит к выбору неэффективного плана (например, долгому циклическому обходу *Nested Loops* вместо быстрого *Hash Match*), что вызывает лавинообразное падение производительности запроса.
Индексация полей связи — залог быстродействия любого запроса. Однако здесь тоже есть нюансы:
ИНДЕКСИРОВАТЬ ПО ... критически важна. Если временная таблица большая и не проиндексирована, СУБД будет вынуждена делать полное сканирование таблицы (*Full Scan*) или строить индекс в базе временных таблиц (tempdb) «на лету» в процессе выполнения запроса.Стоит упомянуть поведение платформы 1С при передаче списка значений или массива напрямую в качестве параметра (ГДЕ Спр.Реквизит В (&Массив)):
WHERE Ref IN (value1, value2...) либо строит дерево условий ИЛИ. Это работает максимально быстро.Универсального «серебряного коня в вакууме» не существует, однако мы можем сформулировать четкие правила выбора конструкции для оптимизации запросов:
ГДЕ — это путает оптимизатор СУБД. Все условия соединения должны находиться строго в секции ПО.ГДЕ (Поле1, Поле2) В (ВЫБРАТЬ Поле1, Поле2 ИЗ ВТ) — это лаконично и отлично оптимизируется платформой 1С и СУБД.