При написании сложных запросов в платформе 1С:Предприятие разработчики иногда сталкиваются с ситуацией, когда запрос с конструкцией ЛЕВОЕ СОЕДИНЕНИЕ, содержащий подзапрос (вложенный запрос), начинает выполняться крайне долго, «зависает» или приводит к падению сеанса. Проанализируем, почему возникает эта проблема и как правильно переписать запрос согласно стандартам разработки 1С.
Рассмотрим ключевые технические факторы, которые приводят к высокой нагрузке на СУБД при использовании вложенных запросов в соединениях:
Объект часто является составным (может хранить ссылки на различные документы и справочники). На уровне СУБД составное поле хранится в виде трех колонок (тип, вид метаданных и GUID). Соединение по такому полю без явного приведения типов заставляет 1С генерировать громоздкий SQL-код с многочисленными проверками всех возможных таблиц, исключая использование существующих индексов.Главный стандарт оптимизации запросов в 1С — отказ от вложенных подзапросов в секциях ПО и ГДЕ в пользу пакета запросов с временными таблицами. Вынесем выборку первичных данных во временную таблицу и проиндексируем поле связи.
Разберем по шагам создание пакета запросов:
ВТ_ПринятыеДокументы.ИНДЕКСИРОВАТЬ ПО по полю соединения. Это позволит СУБД построить быстрый индекс для временной таблицы в TempDB.Посмотрим на пример исправленного запроса:
ВЫБРАТЬ
ПеремещениеТоваров.Ссылка КАК Док,
ПеремещениеТоваров.Дата КАК ДатаДок,
ПеремещениеТоваров.СкладПолучатель КАК СкладПолучатель,
ИсторияСтатусов.Дата КАК Принято
ПОМЕСТИТЬ ВТ_ПринятыеДокументы
ИЗ
РегистрСведений.ИсторияСтатусовСправочниковИДокументов КАК ИсторияСтатусов
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ПеремещениеТоваров КАК ПеремещениеТоваров
ПО ИсторияСтатусов.Объект = ПеремещениеТоваров.Ссылка
ГДЕ
ПеремещениеТоваров.Проведен
И ПеремещениеТоваров.Дата МЕЖДУ &нДата И &кДата
И ПеремещениеТоваров.Организация = &Организация
И ИсторияСтатусов.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТПринято)
ИНДЕКСИРОВАТЬ ПО
Док
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТЗПринято.ДатаДок КАК ДатаДок,
ТЗПринято.Док КАК Док,
ТЗПринято.Принято КАК Принято,
ТЗПринято.СкладПолучатель КАК СкладПолучатель,
ИсторияСтатусовОтгружено.Дата КАК ДатаОтгружено
ИЗ
ВТ_ПринятыеДокументы КАК ТЗПринято
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияСтатусовСправочниковИДокументов КАК ИсторияСтатусовОтгружено
ПО ТЗПринято.Док = ИсторияСтатусовОтгружено.Объект
И (ИсторияСтатусовОтгружено.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТОтгружено))
Если реквизит Объект имеет составной тип, обязательно используем функцию ВЫРАЗИТЬ(). Это сообщает платформе и СУБД конкретную таблицу метаданных, что позволяет существенно сократить генерируемый SQL-код и ускорить выполнение соединений.
Пример приведения составного типа в секции выборки или соединения:
ВЫРАЗИТЬ(ИсторияСтатусов.Объект КАК Документ.ПеремещениеТоваров)
Вместо повторного соединения регистра сведений самого с собой мы можем получить всю необходимую цепочку статусов за один проход по таблице с помощью агрегатных функций и условных выражений.
Рассмотрим структуру такого запроса:
ВЫБРАТЬ
ВЫРАЗИТЬ(ИсторияСтатусов.Объект КАК Документ.ПеремещениеТоваров) КАК Док,
МАКСИМУМ(ВЫБОР
КОГДА ИсторияСтатусов.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТПринято)
ТОГДА ИсторияСтатусов.Дата
КОНЕЦ) КАК ДатаПринято,
МАКСИМУМ(ВЫБОР
КОГДА ИсторияСтатусов.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТОтгружено)
ТОГДА ИсторияСтатусов.Дата
КОНЕЦ) КАК ДатаОтгружено
ПОМЕСТИТЬ ВТ_СтатусыДокументов
ИЗ
РегистрСведений.ИсторияСтатусовСправочниковИДокументов КАК ИсторияСтатусов
ГДЕ
ИсторияСтатусов.Статус В (
ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТПринято),
ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТОтгружено)
)
СГРУППИРОВАТЬ ПО
ВЫРАЗИТЬ(ИсторияСтатусов.Объект КАК Документ.ПеремещениеТоваров)
ИМЕЮЩИЕ
МАКСИМУМ(ВЫБОР
КОГДА ИсторияСтатусов.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТПринято)
ТОГДА ИсторияСтатусов.Дата
КОНЕЦ) ЕСТЬ НЕ NULL
Этот способ позволяет избежать повторных соединений к крупным таблицам и максимально эффективно использует индексы СУБД.
Сформулируем основные правила оптимизации запросов при работе с соединениями и составными типами в 1С:
ЛЕВОЕ СОЕДИНЕНИЕ или ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Выносите их во временные таблицы.ИНДЕКСИРОВАТЬ ПО).ВЫРАЗИТЬ().