При разработке запросов в платформе 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда выборка данных с фильтрацией через подзапрос вида ГДЕ Поле В (ВЫБРАТЬ ...) выполняется существенно дольше, чем аналогичная выборка с предварительным помещением данных во временную таблицу. Разберем по шагам, почему так происходит, как оптимизатор СУБД обрабатывает такие конструкции и как правильно переписать запрос по стандартам разработки 1С.
Рассмотрим исходные примеры запросов, вызвавшие вопрос о разнице в скорости работы.
Вариант 1 (медленный):
ВЫБРАТЬ
РеализацияТоваровУслуг.Ссылка КАК ДокументРТУ
ИЗ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
РеализацияТоваровУслуг.Склад В
(ВЫБРАТЬ
Склады.Ссылка КАК Склад
ИЗ
Справочник.Склады КАК Склады)
Вариант 2 (более быстрый):
ВЫБРАТЬ
Склады.Ссылка КАК Склад
ПОМЕСТИТЬ ВТ_СКЛАДЫ
ИЗ
Справочник.Склады КАК Склады
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
РеализацияТоваровУслуг.Ссылка КАК ДокументРТУ
ИЗ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
РеализацияТоваровУслуг.Склад В
(ВЫБРАТЬ
ВТ_СКЛАДЫ.Склад
ИЗ
ВТ_СКЛАДЫ КАК ВТ_СКЛАДЫ)
Выясним причину, почему первый вариант может демонстрировать низкую производительность и приводить к неожиданным задержкам:
Nested Loops) со сканированием таблицы вместо эффективного Hash Match или Merge Join. В худшем сценарии это действительно может приводить к многократному чтению таблицы справочника для строк основного документа.IN при наличии неопределенных значений (NULL) во вложенной выборке использует трехзначную логику (TRUE, FALSE, UNKNOWN). Чтобы гарантировать корректность вычислений, СУБД не всегда может трансформировать подзапрос в полусоединение (Left Semi Join), из-за чего план выполнения существенно усложняется.ГДЕ при наложении условий безопасности разрастается в громоздкое дерево выражений, создавая колоссальную нагрузку на парсер СУБД и приводя к деградации производительности.Проанализируем ситуацию со вторым вариантом. Когда мы используем конструкцию ПОМЕСТИТЬ, платформа 1С создает физическую временную таблицу в базе данных СУБД (например, в tempdb для MS SQL Server).
В этот момент происходят следующие ключевые процессы:
Хотя второй вариант работает быстрее, использование подзапроса к временной таблице в блоке ГДЕ ... В все еще не является идеальным решением. Согласно стандартам разработки фирмы «1С», для максимальной производительности следует применять два правила: индексировать временные таблицы и заменять предикаты фильтрации на внутренние соединения.
Разберем по шагам создание эталонного запроса:
ИНДЕКСИРОВАТЬ ПО.ГДЕ ... В на ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Это позволяет СУБД задействовать быстрый поиск по индексу (Index Seek).Ссылка справочника уникальность гарантирована платформой, но для составных выборок следует применять РАЗЛИЧНЫЕ или группировку.Посмотрим на пример итогового оптимизированного запроса:
ВЫБРАТЬ
Склады.Ссылка КАК Склад
ПОМЕСТИТЬ ВТ_СКЛАДЫ
ИЗ
Справочник.Склады КАК Склады
ИНДЕКСИРОВАТЬ ПО
Склад
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Реализация.Ссылка КАК ДокументРТУ
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_СКЛАДЫ КАК ВТ_СКЛАДЫ
ПО Реализация.Склад = ВТ_СКЛАДЫ.Склад
Такой подход обеспечивает минимальное потребление ресурсов сервера, исключает ошибки оценки кардинальности оптимизатором СУБД и гарантирует максимальное быстродействие выборки даже на высоконагруженных информационных базах большого объема.