#std655¶
Ограничения на соединения с вложенными запросами и виртуальными таблицами¶
1.1.¶
В запросах не используйте соединения с вложенными запросами. Соединяйте только объекты метаданных или временные таблицы.
Если в запросе есть соединение с вложенным запросом, переписывайте его с #std777: использованием временных таблиц. Это правило действует независимо от того, с какой стороны соединения находится вложенный запрос.
Исключение: вложенный запрос гарантированно сканирует мало записей.
Соединение с вложенным запросом может привести к проблемам:
- крайне медленное выполнение даже при слабой загрузке сервера (вплоть до замедления на несколько порядков);
- нестабильная скорость (в одних условиях быстро, в других - очень медленно);
- существенная разница времени выполнения на разных СУБД;
- чувствительность к актуальности и полноте статистик (после обновления статистик быстро, затем снова медленно).
Потенциально опасный запрос
Уточнение
Оптимизатор СУБД (независимо от конкретной СУБД) не всегда корректно оптимизирует такие запросы. Ключевая сложность - выбор способа соединения.
Для соединения выборок существует несколько алгоритмов. Правильный выбор зависит от того, сколько строк ожидается в каждой выборке.
Если соединяются две физические таблицы, СУБД обычно может оценить их объем по статистике. Если одна из выборок - вложенный запрос, оценка его объема становится намного менее точной. Из-за этого СУБД может выбрать неверный план, что приводит к резкому падению производительности.
1.2.¶
Для ситуации из п. 1.1 используйте пакетный запрос и временную таблицу:
Правильно
// Создать менеджер временных таблиц
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Текст пакетного запроса
Запрос.Текст = "
| // Заполняем временную таблицу. Запрос к регистру лимитов.
| ВЫБРАТЬ ...
| ПОМЕСТИТЬ Лимиты
| ИЗ РегистрСведений.Лимиты
| ГДЕ ...
| СГРУППИРОВАТЬ ПО ...
| ИНДЕКСИРОВАТЬ ПО ...;
|
| // Выполняем основной запрос с использованием временной таблицы
| ВЫБРАТЬ ...
| ИЗ Документ.РеализацияТоваровУслуг
| ЛЕВОЕ СОЕДИНЕНИЕ Лимиты
| ПО ...;";
Такой подход:
- упрощает работу оптимизатора (в соединении участвуют физические таблицы с понятным объемом);
- повышает стабильность плана выполнения;
- дает более предсказуемую производительность на разных СУБД;
- улучшает читаемость и упрощает отладку запроса.
2.¶
Если запрос соединяется с виртуальной таблицей языка запросов 1С:Предприятия
(например, РегистрНакопления.Товары.Остатки)
и работает медленно, вынесите обращение к виртуальной таблице
в отдельный запрос с сохранением результата во временную таблицу
(см. п. 1.1).
3.¶
Избегайте неявных подзапросов, которые появляются из-за вложенных соединений.
Неправильно
По сути это эквивалентно соединению с подзапросом:
ВЫБРАТЬ ...
ИЗ Справочник.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ ...
ИЗ РегистрНакопления.ТоварыНаСкладах
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций
ПО ...)
ПО ...
Используйте последовательные соединения:
Правильно
Важно: вложенные и последовательные соединения - разные запросы, они могут давать разный результат.
- если вложенное соединение использовано по ошибочной аналогии, перепишите его на последовательное;
- если вложенное соединение использовано осознанно, от него лучше отказаться, так как оно может существенно снизить производительность;
- при необходимости можно заменить его соединением с временной таблицей, но сначала проверьте вариант с последовательным соединением - обычно он эффективнее.
См. также¶
- #std656: Ограничения на использование вложенных запросов в условии соединения
- #std777: Использование временных таблиц
Проверки¶
#bslls:JoinWithSubQuery #bslls:JoinWithVirtualTable