Что быстрее в запросах 1С: оператор "В" с подзапросом или соединение таблиц?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Кажется, что оба варианта решают одну и ту же задачу, но под капотом СУБД выполняет эти операции совершенно по-разному. Разберем детально физику процессов, разницу в логике работы и выясним, какой вариант и в каких сценариях будет работать быстрее.

Анализ исходных вариантов запроса

Для начала проанализируем примеры запросов, которые часто вызывают споры среди разработчиков. Первый вариант использует конструкцию ГДЕ ... В (ВЫБРАТЬ ...):


ВЫБРАТЬ
    Спр.Ссылка КАК Ссылка
ИЗ
    Справочник.Справочник КАК Спр
ГДЕ
    Спр.Реквизит В (ВЫБРАТЬ ТЗ.Ссылка ИЗ ВТ_ТЗ КАК ТЗ)

Второй вариант, предложенный в обсуждении, использует левое соединение с условием в секции ГДЕ:


ВЫБРАТЬ
    Спр.Ссылка КАК Ссылка
ИЗ
    ВТ_ТЗ КАК ТЗ
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Справочник КАК Спр
    ПО (1=1) // или без явного ПО
ГДЕ
    ТЗ.Ссылка = Спр.Реквизит

Важное методическое замечание: Второй вариант написания запроса является грубой ошибкой проектирования. Наложение жесткого фильтра на правую таблицу в секции ГДЕ фактически превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Однако СУБД при этом тратит лишние ресурсы на построение плана, а оптимизатор может запутаться. Если нам нужно сопоставить данные, правильным решением будет использование явного внутреннего соединения с условием связи в секции ПО:


ВЫБРАТЬ
    Спр.Ссылка КАК Ссылка
ИЗ
    ВТ_ТЗ КАК ТЗ
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Справочник КАК Спр
    ПО ТЗ.Ссылка = Спр.Реквизит

Теперь сравним корректный вариант с использованием оператора В и классическое ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

Различие 1: Проблема дублирования данных (Semi-Join vs JOIN)

Главное логическое различие между этими конструкциями заключается в обработке дублирующихся строк во временной таблице источника (ВТ_ТЗ).

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

Таким образом, если во временной таблице гарантированно нет дубликатов (например, она предварительно сгруппирована), оба варианта вернут одинаковое количество строк. Если же уникальность не гарантирована, использование оператора В безопаснее и логически чище.

Различие 2: Поведение оптимизатора СУБД на разных объемах данных

Выбор оптимального метода сильно зависит от размера временной таблицы, которая используется для фильтрации.

Маленький объем данных (до нескольких сотен строк):
При небольшом объеме временной таблицы оператор В работает очень быстро. СУБД часто преобразует такой запрос в план с использованием физического оператора *Nested Loops* (вложенные циклы) с поиском по индексу справочника. Производительность в данном случае будет практически идентичной или немного в пользу В.

Большой объем данных (тысячи и десятки тысяч строк):
При работе с большими наборами данных оптимизаторы современных СУБД (MS SQL Server, PostgreSQL) отдают предпочтение явному **Внутреннему соединению**. В этом случае СУБД проще построить стабильный план на основе эффективных физических операторов, таких как *Merge Join* (соединение слиянием) или *Hash Match* (хэш-соединение).
Если использовать оператор В (ВЫБРАТЬ...) на больших таблицах, оптимизатор СУБД может ошибиться в оценке кардинальности (количества строк) вложенного подзапроса. Ошибка в оценке приводит к выбору неэффективного плана (например, долгому циклическому обходу *Nested Loops* вместо быстрого *Hash Match*), что вызывает лавинообразное падение производительности запроса.

Требования к индексации временных таблиц

Индексация полей связи — залог быстродействия любого запроса. Однако здесь тоже есть нюансы:

Автоматическое поведение платформы 1С при передаче массивов

Стоит упомянуть поведение платформы 1С при передаче списка значений или массива напрямую в качестве параметра (ГДЕ Спр.Реквизит В (&Массив)):

  1. При небольшом размере массива (обычно до нескольких десятков или сотен элементов) платформа транслирует условие напрямую в SQL-запрос как перечисление литералов: WHERE Ref IN (value1, value2...) либо строит дерево условий ИЛИ. Это работает максимально быстро.
  2. При превышении внутреннего лимита размера массива платформа 1С автоматически «под капотом» создает временную таблицу в СУБД, наполняет ее элементами переданного массива и неявно выполняет соединение.

Резюме и рекомендации

Универсального «серебряного коня в вакууме» не существует, однако мы можем сформулировать четкие правила выбора конструкции для оптимизации запросов:

← На главную