Соединения в запросах — без подзапросов и виртуальных таблиц
Директивный
диагностикаJoinWithSubQueryдиагностикаJoinWithVirtualTableдиагностикаFieldsFromJoinsWithoutIsNullдиагностикаLogicalOrInJoinQuerySection
Правило
Заголовок раздела «Правило»- Не соединяться с подзапросом. Результат подзапроса сначала помещается во временную таблицу (при необходимости — с индексом по полям соединения), и соединение выполняется с ней.
- Не соединяться с виртуальной таблицей (
.Остатки,.СрезПоследнихи т.п.) — её результат так же выносится во временную таблицу. - Поля, полученные левым/правым соединением, оборачивать в
ЕСТЬNULL(...)или явно проверятьЕСТЬ NULL— иначеNULLрасползается по расчётам и группировкам. - Не использовать
ИЛИв условиях соединения (ПО ... ИЛИ ...) — оптимизатор теряет индексы; переформулировать через объединение или условие вГДЕ.
СУБД не имеет статистики по результату подзапроса и виртуальной таблицы, поэтому оптимизатор строит план вслепую: типичный итог — вложенные циклы по огромной выборке, и запрос, работавший секунду на тесте, виснет на минутах на боевых объёмах. Временная таблица даёт оптимизатору материализованный набор со статистикой и предсказуемый план. Это одна из самых частых причин «отчёт не открывается на проде».
NULL тихо ломает и условия
Заголовок раздела «NULL тихо ломает и условия»Опасность NULL не только в расчётах: сравнение NULL с чем угодно — не Истина,
поэтому условие в ГДЕ по полю левого соединения молча выбрасывает строки без
пары. Классика — отчёт «дефицит» пропускает как раз худший случай:
Тот же эффект в обратную сторону: условие вида Остатки.Поле = &Значение в ГДЕ
превращает левое соединение во внутреннее — если условие относится к
присоединяемой таблице, его место в секции ПО или внутри временной таблицы.
Как обнаружить
Заголовок раздела «Как обнаружить»Явные случаи ловят диагностики BSL LS из фронтматтера этой страницы (соединение
с подзапросом/виртуальной таблицей, поле без ЕСТЬNULL, ИЛИ в ПО). Уже
работающий медленный запрос диагностируется планом: типичный признак — nested loops по большой выборке там, где ожидался хеш/merge-join по индексу. Проверять
на объёмах, сопоставимых с боевыми, — на двух тестовых записях любой план быстрый
(см. перф-прогон горячих мест).
Когда не применять
Заголовок раздела «Когда не применять»- Заведомо маленькие выборки в разовых обработках — временная таблица не даст выигрыша; но в коде конфигурации (отчёты, проведение, обмены) правило действует всегда: объём данных вырастет без вас.
ЕСТЬNULLне нужен, еслиNULL-поле тут же отфильтровывается условиемЕСТЬ NULL / ЕСТЬ НЕ NULLвГДЕ.