#std655

Ограничения на соединения с вложенными запросами и виртуальными таблицами

1.1.

В запросах не используйте соединения с вложенными запросами. Соединяйте только объекты метаданных или временные таблицы.

Если в запросе есть соединение с вложенным запросом, переписывайте его с #std777: использованием временных таблиц. Это правило действует независимо от того, с какой стороны соединения находится вложенный запрос.

Исключение: вложенный запрос гарантированно сканирует мало записей.

Соединение с вложенным запросом может привести к проблемам:

  • крайне медленное выполнение даже при слабой загрузке сервера (вплоть до замедления на несколько порядков);
  • нестабильная скорость (в одних условиях быстро, в других - очень медленно);
  • существенная разница времени выполнения на разных СУБД;
  • чувствительность к актуальности и полноте статистик (после обновления статистик быстро, затем снова медленно).

Потенциально опасный запрос

ВЫБРАТЬ ...
ИЗ Документ.РеализацияТоваровУслуг
ЛЕВОЕ СОЕДИНЕНИЕ (
    ВЫБРАТЬ ИЗ РегистрСведений.Лимиты
    ГДЕ ...
    СГРУППИРОВАТЬ ПО ...
) ПО ...

Уточнение

Оптимизатор СУБД (независимо от конкретной СУБД) не всегда корректно оптимизирует такие запросы. Ключевая сложность - выбор способа соединения.

Для соединения выборок существует несколько алгоритмов. Правильный выбор зависит от того, сколько строк ожидается в каждой выборке.

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

1.2.

Для ситуации из п. 1.1 используйте пакетный запрос и временную таблицу:

Правильно

// Создать менеджер временных таблиц
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Текст пакетного запроса
Запрос.Текст = "
|   // Заполняем временную таблицу. Запрос к регистру лимитов.
|   ВЫБРАТЬ ...
|   ПОМЕСТИТЬ Лимиты
|   ИЗ РегистрСведений.Лимиты
|   ГДЕ ...
|   СГРУППИРОВАТЬ ПО ...
|   ИНДЕКСИРОВАТЬ ПО ...;
|
|   // Выполняем основной запрос с использованием временной таблицы
|   ВЫБРАТЬ ...
|   ИЗ Документ.РеализацияТоваровУслуг
|   ЛЕВОЕ СОЕДИНЕНИЕ Лимиты
|   ПО ...;";

Такой подход:

  • упрощает работу оптимизатора (в соединении участвуют физические таблицы с понятным объемом);
  • повышает стабильность плана выполнения;
  • дает более предсказуемую производительность на разных СУБД;
  • улучшает читаемость и упрощает отладку запроса.
2.

Если запрос соединяется с виртуальной таблицей языка запросов 1С:Предприятия (например, РегистрНакопления.Товары.Остатки) и работает медленно, вынесите обращение к виртуальной таблице в отдельный запрос с сохранением результата во временную таблицу (см. п. 1.1).

3.

Избегайте неявных подзапросов, которые появляются из-за вложенных соединений.

Неправильно

ВЫБРАТЬ ...
ИЗ Справочник.Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций
        ПО ...
    ПО ...

По сути это эквивалентно соединению с подзапросом:

ВЫБРАТЬ ...
ИЗ Справочник.Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ ...
        ИЗ РегистрНакопления.ТоварыНаСкладах
            ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций
            ПО ...)
    ПО ...

Используйте последовательные соединения:

Правильно

ВЫБРАТЬ ...
ИЗ Справочник.Номенклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах
        ПО ...
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций
        ПО ...

Важно: вложенные и последовательные соединения - разные запросы, они могут давать разный результат.

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

#bslls:JoinWithSubQuery #bslls:JoinWithVirtualTable

#acc:494

Источник

https://its.1c.ru/db/v8std#content:655