Что быстрее в запросах 1С: использование оператора «В» или «ЛЕВОЕ СОЕДИНЕНИЕ»?

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

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

Вариант 1: Использование оператора «В» с подзапросом

Рассмотрим классическую ситуацию, когда сначала мы выбираем ссылки на документы во временную таблицу по определенному условию, а затем используем эту таблицу для фильтрации данных в другом запросе. Рассмотрим пример кода:


// Шаг 1: Подготовка временной таблицы
ВЫБРАТЬ 
    МД.Ссылка 
ПОМЕСТИТЬ вт_МоиДокументы 
ИЗ 
    Документ.МойДокумент КАК МД 
ГДЕ 
    НачалоПериода(МД.Дата, ДЕНЬ) = НачалоПериода(&Дата, ДЕНЬ);

// Шаг 2: Использование оператора В
ВЫБРАТЬ 
    ТаблицаМоегоДокумента.Ссылка, 
    ТаблицаМоегоДокумента.Номенклатура 
ИЗ 
    Документ.МойДокумент.Состав КАК ТаблицаМоегоДокумента 
ГДЕ 
    ТаблицаМоегоДокумента.Ссылка В (ВЫБРАТЬ Ссылка ИЗ вт_МоиДокументы)

Проанализируем этот подход. С точки зрения СУБД (SQL Server или PostgreSQL), оператор В (IN) при работе с подзапросом чаще всего преобразуется в так называемый Semi-Join (полусоединение). Его ключевая особенность заключается в том, что СУБД прекращает поиск совпадений в правой таблице (в нашем случае вт_МоиДокументы), как только найдена первая подходящая запись. Это гарантирует, что в итоговой выборке не будет дублей, даже если во временной таблице по какой-то причине оказались одинаковые ссылки.

Однако стоит помнить, что оператор В может быть «тяжелым», если временная таблица содержит огромное количество записей (десятки и сотни тысяч), а индексы отсутствуют. В некоторых случаях оптимизатор может построить план с вложенными циклами (Nested Loops), что замедлит работу на больших объемах данных.

Вариант 2: Использование «ЛЕВОГО СОЕДИНЕНИЯ»

Теперь разберем альтернативный вариант, когда мы соединяем временную таблицу с целевой таблицей части документа:


ВЫБРАТЬ 
    ТаблицаМоегоДокумента.Ссылка, 
    ТаблицаМоегоДокумента.Номенклатура 
ИЗ 
    вт_МоиДокументы КАК вт_МоиДокументы 
ЛЕВОЕ СОЕДИНЕНИЕ 
    Документ.МойДокумент.Состав КАК ТаблицаМоегоДокумента 
ПО 
    вт_МоиДокументы.Ссылка = ТаблицаМоегоДокумента.Ссылка

При использовании ЛЕВОГО СОЕДИНЕНИЯ важно понимать, что результат может отличаться от варианта с оператором В. Если в правой таблице (табличной части) одному документу соответствует несколько строк, соединение вернет их все. Но если мы перепутаем таблицы местами или будем использовать ВНУТРЕННЕЕ СОЕДИНЕНИЕ, мы рискуем получить дублирование строк из основной таблицы, если связи не уникальны.

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

Оптимизация условий ГДЕ: проблема функций над полями

Выясним причину, по которой запрос может работать медленно (как в примере автора — 86 секунд). Одной из самых частых ошибок является использование функций над полями таблиц в условиях ГДЕ. Рассмотрим конструкцию:

ГДЕ НачалоПериода(МД.Дата, ДЕНЬ) = НачалоПериода(&Дата, ДЕНЬ)

Хотя платформа 1С пытается оптимизировать это выражение, для СУБД это часто выглядит как невозможность использования индекса по полю Дата. Это называется нарушением SARGability (Search Argumentable). Чтобы индекс работал максимально эффективно, поле должно сравниваться с чистым значением или параметром. Проанализируем правильный вариант:


// Правильный подход к фильтрации по датам
ГДЕ 
    МД.Дата МЕЖДУ &НачалоДня И &КонецДня

В этом случае SQL-сервер мгновенно находит нужный диапазон в индексе, не пересчитывая функцию для каждой из миллионов строк таблицы документа.

Индексация временных таблиц: когда это нужно?

Рассмотрим еще один важный аспект — индексацию временных таблиц. Многие программисты забывают, что временная таблица в SQL — это такая же таблица, требующая структуры для быстрого поиска. Проанализируем следующие правила:

  1. Если во временной таблице содержится менее 100 записей, индексация может быть избыточной, так как время на создание индекса превысит время простого сканирования таблицы в памяти.
  2. Если записей более 200-500, индекс по полю Ссылка (или другому полю связи) становится критически важным.
  3. При использовании ВНУТРЕННЕГО СОЕДИНЕНИЯ наличие индекса позволяет СУБД использовать эффективный алгоритм Index Seek вместо Table Scan.

Пример создания проиндексированной ВТ:


ВЫБРАТЬ 
    МД.Ссылка КАК Ссылка
ПОМЕСТИТЬ вт_МоиДокументы 
ИЗ 
    Документ.МойДокумент КАК МД 
ГДЕ 
    МД.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
ИНДЕКСИРОВАТЬ ПО 
    Ссылка;

Обращения «через точку» и их последствия

Разберем ситуацию из сообщения №3 форума, где предлагается обращаться к дате через ссылку: ТаблицаМоегоДокумента.Ссылка.Дата. Посмотрим на это глазами оптимизатора 1С:

Каждое такое обращение порождает неявное ЛЕВОЕ СОЕДИНЕНИЕ с основной таблицей документа. Если ваше условие стоит в ГДЕ, то сначала будут соединены все строки табличной части с основными данными документов, и только потом произойдет фильтрация. Это крайне неэффективно. Всегда старайтесь сначала отобрать нужные ссылки во временную таблицу, а затем соединяться с «тяжелыми» таблицами (табличными частями, регистрами).

Почему запрос все равно идет 86 секунд?

Если вы применили все вышеперечисленные советы (использовали МЕЖДУ, проиндексировали ВТ, заменили В на JOIN), а скорость не изменилась, стоит обратить внимание на следующие факторы:

  1. Устаревшая статистика СУБД: Если данные в таблицах часто меняются, оптимизатор может ошибочно выбирать неверный план (например, думать, что строк мало, и использовать циклы вместо хеш-соединений). Попробуйте выполнить обновление статистик (команда UPDATE STATISTICS в SQL или регламентное задание 1С).
  2. Чтение лишних полей: Проанализируем список полей в ВЫБРАТЬ. Если там есть поля типа ХранилищеЗначения, длинные строки или неограниченные строки, основное время может уходить на передачу данных по сети, а не на сам поиск.
  3. Разрастание TempDB: При работе с огромными временными таблицами узким местом становится дисковая подсистема сервера, где хранится системная база tempdb. В этом случае даже самый оптимальный запрос будет ждать своей очереди на чтение с диска.

Итоговые рекомендации по выбору

Проанализировав ситуацию, мы можем сделать следующие выводы:

← На главную