К статье
Практический материал

Разработка и Конфигурация · 12 мин

Что быстрее в 1С: внутреннее или левое соединение

Разбираем, что быстрее в запросах 1С — внутреннее или левое соединение, как СУБД выполняет соединения, когда выбор влияет на скорость и как проверить план запроса.

Вопрос «что быстрее — внутреннее или левое соединение» возникает у каждого разработчика 1С, когда отчёт начинает выполняться минутами. Короткий ответ: при прочих равных внутреннее соединение (INNER JOIN) обычно выполняется быстрее или так же, как левое (LEFT JOIN), потому что оптимизатор СУБД получает больше свободы — он может отбросить лишние строки раньше и переставлять таблицы местами. Но разница редко бывает решающей сама по себе: гораздо сильнее на скорость влияют индексы по полям соединения, селективность условий и объём обрабатываемых данных.

Важно понимать главное: внутреннее и левое соединение — это не два варианта одной операции, а разная логика. Внутреннее соединение возвращает только пары, где совпадение найдено в обеих таблицах. Левое возвращает все строки левой таблицы, а там, где пары нет, подставляет NULL. Поэтому заменять одно другим можно только тогда, когда бизнес-логика это допускает. Если вам нужны все документы, включая те, у которых не заполнен контрагент, — внутреннее соединение просто потеряет часть данных, и вопрос скорости станет бессмысленным.

Ниже разберём, как платформа 1С транслирует соединения в SQL, в каких сценариях левое соединение реально тормозит, как безопасно заменить его на внутреннее и как проверить результат через план запроса, а не «на глаз».

Как 1С и СУБД выполняют соединения

Запрос на встроенном языке 1С платформа транслирует в SQL для используемой СУБД — чаще всего Microsoft SQL Server или PostgreSQL. Конструкция ЛЕВОЕ СОЕДИНЕНИЕ превращается в LEFT JOIN, ВНУТРЕННЕЕ СОЕДИНЕНИЕ — в INNER JOIN. Дальше работает оптимизатор СУБД: он строит план запроса, выбирает порядок обхода таблиц и алгоритм соединения (вложенные циклы, хеш-соединение, соединение слиянием).

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

Дополнительная особенность 1С: виртуальные таблицы (остатки, срезы, обороты) при соединении разворачиваются в подзапросы к физическим таблицам итогов. Если соединять два виртуальных таблицы левым соединением без условий, СУБД может построить крайне тяжёлый план с полным сканированием. Это одна из самых частых причин «тормозящих» запросов в отчётах.

В каких сценариях левое соединение медленнее

Первый сценарий — соединение больших таблиц без предварительной фильтрации. Если левая таблица содержит миллионы строк, а условие отбора стоит только в секции ГДЕ по полям правой таблицы, СУБД вынуждена сначала сформировать все строки левой таблицы с NULL-ами, а потом отбрасывать лишнее. Внутреннее соединение в той же ситуации позволило бы отфильтровать на раннем этапе.

Второй сценарий — цепочки левых соединений. Когда в запросе три-пять ЛЕВЫХ СОЕДИНЕНИЙ подряд, каждое следующее работает с уже раздутым промежуточным результатом. Оптимизатору сложно переупорядочить такую конструкцию, и время выполнения растёт нелинейно. Замена части левых соединений на внутренние (там, где логика позволяет) часто даёт заметный эффект именно в таких цепочках.

Третий сценарий — левое соединение с виртуальной таблицей без параметров. Например, левое соединение регистра накопления с виртуальной таблицей «Остатки» без указания периода и отборов. Виртуальная таблица материализуется целиком, и соединение превращается в обработку полного среза итогов.

Когда разницы практически нет

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

Когда левое соединение можно заменить на внутреннее

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

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

Перед заменой обязательно проверьте данные: выполните контрольный запрос, который считает строки левой таблицы без пары. Если таких строк нет или они не нужны — заменяйте. Если есть и нужны — оставляйте левое соединение и ищите выигрыш в индексах и фильтрации.

Что влияет на скорость сильнее типа соединения

Практика показывает, что тип соединения — далеко не главный фактор производительности запроса. Значительно сильнее влияют следующие вещи, которые стоит проверить в первую очередь.

  • Индексы по полям соединения. Соединение по неиндексированному полю заставляет СУБД сканировать таблицу целиком. В 1С индексирование включается в свойствах реквизита (признак «Индексировать») либо создаётся автоматически для измерений регистров и стандартных реквизитов.
  • Фильтрация до соединения. Условия по периоду, организации, складу должны отсекать данные как можно раньше — в параметрах виртуальных таблиц или во вложенных запросах, а не после всех соединений.
  • Параметры виртуальных таблиц. Для «Остатки», «Обороты», «СрезПоследних» всегда задавайте период и условия в параметрах — это резко сокращает объём материализуемых данных.
  • Отсутствие соединений с подзапросами и таблицами значений без индексов. Временная таблица, помещённая в менеджер временных таблиц без индекса по полю соединения, — классическая причина медленного пакетного запроса.
  • Количество выбираемых полей. Выборка «звёздочкой» или лишних полей увеличивает объём передаваемых данных, особенно при соединении с таблицами, содержащими поля неограниченной длины.

Как проверить результат: план запроса и замеры

Не оценивайте оптимизацию «на глаз» по одному запуску. Правильная проверка включает три шага: замер времени до изменения, замер после и сравнение планов запроса. План покажет, какой алгоритм соединения выбрала СУБД, используются ли индексы и где возникают дорогие операции сканирования.

В 1С план запроса можно посмотреть через технологический журнал (события DBMSSQL/DBPOSTGRS с планами) или через инструменты разработчика и внешние консоли запросов с выводом плана. На стороне СУБД для MS SQL Server используется отображение фактического плана выполнения в Management Studio, для PostgreSQL — команда EXPLAIN ANALYZE. Обращайте внимание на операторы Scan (плохо при больших таблицах) против Seek (хорошо) и на оценочное число строк на каждом шаге.

Замеры делайте на копии базы или в тестовом контуре с данными, близкими по объёму к рабочим. Запрос, выполняющийся за 50 мс на пустой базе, на рабочей может работать минуты — и наоборот, оптимизация, не дающая эффекта на тестовых данных, иногда критична на продуктивных объёмах.

Порядок действий при оптимизации соединения
  1. 1
    Зафиксируйте текущее время выполнения запроса на реальных объёмах данных
  2. 2
    Проверьте контрольным запросом, есть ли строки левой таблицы без пары в правой
  3. 3
    Если строк без пары нет или они не нужны — замените ЛЕВОЕ СОЕДИНЕНИЕ на ВНУТРЕННЕЕ
  4. 4
    Перенесите условия отбора в параметры виртуальных таблиц и в секции до соединения
  5. 5
    Добавьте ИНДЕКСИРОВАТЬ ПО для временных таблиц по полям соединения
  6. 6
    Сравните план запроса и время до и после: ищите исчезновение Scan и снижение оценочного числа строк

Типичные ошибки при работе с соединениями

Разработчики, оптимизирующие запросы, регулярно наступают на одни и те же грабли. Ниже — самые частые ошибки и их последствия.

Типичные ошибки
01
Замена левого соединения на внутреннее без проверки данных — отчёт ускоряется, но теряет строки, и расхождение всплывает только при сверке с учётом
02
Условие по полю правой таблицы в секции ГДЕ при левом соединении — соединение фактически работает как внутреннее, но с лишними затратами; переносите такие условия в секцию ПО или меняйте тип соединения
03
Соединение с виртуальной таблицей без параметров периода и отборов — материализуется полный срез итогов, и тип соединения уже не играет роли
04
Соединение по составному полю или по полю с функцией (например, ПОДСТРОКА) — индекс не используется, план деградирует до полного сканирования
05
Цепочка из нескольких левых соединений там, где достаточно одного внутреннего с предварительной фильтрацией во временной таблице — промежуточный результат раздувается на каждом шаге

Отдельно стоит упомянуть соединение с таблицей значений, переданной как параметр. Платформа материализует её во временную таблицу, и если не указать индексацию, соединение с ней будет выполняться перебором. Для больших таблиц значений это может стать узким местом всего запроса.

Практический маршрут выбора типа соединения

Итоговая логика выбора проста: сначала определяете, какие строки должны попасть в результат, и только потом думаете о скорости. Тип соединения — это семантика запроса, а не ручка настройки производительности.

Маршрут решения

Как выбрать тип соединения

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

Помните и об альтернативе: иногда вместо левого соединения с проверкой на NULL выгоднее использовать конструкцию НЕ В (ВЫБРАТЬ ...) или объединение через ОБЪЕДИНИТЬ ВСЕ, а иногда — разбить один тяжёлый запрос на пакетный с временными таблицами. Универсального рецепта нет, решение принимается по плану запроса на ваших данных.

Итог

Что важно запомнить

Внутреннее соединение обычно не медленнее левого, потому что даёт оптимизатору больше свободы, но выбирать его нужно по логике данных. Главные резервы скорости — индексы по полям соединения, ранняя фильтрация и параметры виртуальных таблиц. Любую замену типа соединения проверяйте контрольным запросом на полноту данных и планом выполнения.

Частые вопросы

Всегда ли внутреннее соединение быстрее левого?

Нет, не всегда. На небольших таблицах с индексами разница практически незаметна. Заметный выигрыш появляется на больших объёмах и в цепочках соединений, где левое соединение ограничивает оптимизатор в выборе порядка обхода таблиц. Но даже там правильная индексация часто нивелирует разницу.

Можно ли заменить левое соединение на внутреннее без потери данных?

Только если гарантировано, что каждой строке левой таблицы соответствует строка правой, либо строки без пары не нужны в результате. Перед заменой выполните контрольный запрос с условием «правая таблица ЕСТЬ NULL» — если он возвращает строки, которые вам нужны, заменять нельзя.

Почему запрос с левым соединением и условием ГДЕ работает медленно?

Если условие в секции ГДЕ накладывается на поле правой таблицы, строки с NULL отбрасываются после соединения — то есть СУБД делает работу левого соединения, а результат получается как у внутреннего. Перенесите условие в секцию ПО соединения или замените тип на внутреннее.

Как посмотреть план запроса в 1С?

Через технологический журнал с включёнными событиями СУБД и планами, через консоли запросов с функцией вывода плана или напрямую средствами СУБД: фактический план выполнения в SQL Server Management Studio либо EXPLAIN ANALYZE в PostgreSQL. Смотрите на операции сканирования и оценочное число строк.

Что оптимизировать в первую очередь, если соединение тормозит?

Сначала проверьте индексы по полям соединения и задайте параметры виртуальных таблиц (период, отборы). Затем убедитесь, что фильтрация происходит до соединения, а временные таблицы проиндексированы. Тип соединения меняйте в последнюю очередь и только при сохранении логики результата.

Влияет ли тип СУБД на разницу между соединениями?

Да, оптимизаторы MS SQL Server, PostgreSQL и Oracle по-разному строят планы, и на одной СУБД разница между типами соединений может быть заметнее, чем на другой. Поэтому выводы об оптимизации делайте по плану запроса именно на той СУБД и тех данных, где работает ваша база.