При разработке отчетов и обработок в 1С часто требуется собрать срез данных сразу из нескольких периодических регистров сведений: цен, дополнительных реквизитов и ассортиментных ограничений. Неправильно составленный пакетный запрос может приводить к катастрофическому падению производительности на СУБД, вызывая избыточное сканирование таблиц, блокировки и разрастание временных таблиц.
Разберем исходный пример, проанализируем допущенные ошибки и по шагам построим оптимизированный вариант запроса.
Рассмотрим фрагмент исходного кода и выделим критические ошибки, влияющие на скорость выполнения:
ПО НоменклатураПоТипуЦен.Номенклатура.Ссылка = ДополнительныеПараметры... приводит к лишнему неявному левому соединению с таблицей справочника номенклатуры. Так как поле Номенклатура само по себе является ссылкой, обращение к реквизиту .Ссылка избыточно.СрезПоследних без фильтра по измерениям платформа вынуждена рассчитывать срез для всей таблицы регистра. Если мы уже получили список нужных позиций номенклатуры в первой временной таблице, этот список обязательно нужно передавать в условия среза через конструкцию ГДЕ Номенклатура В (...) в параметрах виртуальной таблицы.Контрагент: ПО АссортПостав.ОсновнойПоставщик = ЦеныСрезПоследних.Контрагент. Это критическая ошибка: без связи по полю Номенклатура СУБД выполняет декартово произведение строк по контрагенту, возвращая все существующие цены поставщика на все товары подряд.ИНДЕКСИРОВАТЬ ПО заставляет СУБД строить неоптимальные планы выполнения (например, Nested Loops со сканированием вместо Hash Match / Merge Join).MAX(Период). Если нам нужен только перечень позиций, для которых регистрировались цены, достаточно обратиться к физической таблице регистра с отбором и ключевым словом РАЗЛИЧНЫЕ.Разберем процесс исправления запроса шаг за шагом.
Если требуется получить позиции номенклатуры, по которым установлены цены определенного типа, мы отбираем их и сразу индексируем поле связи. Обязательно используем РАЗЛИЧНЫЕ, чтобы избежать дублирования строк:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Цены.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_Номенклатура
ИЗ
РегистрСведений.Цены КАК Цены
ГДЕ
Цены.ТипЦен = &ТипЦен
ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
При соединении с регистрами сведений передаем список отобранной номенклатуры непосредственно в параметры виртуальных таблиц СрезПоследних. Это ограничит объем рассчитываемых итогов на стороне СУБД:
ВЫБРАТЬ
ВТ_Номенклатура.Номенклатура КАК Номенклатура,
Ограничения.Состояние КАК Состояние,
ПараметрыНоменклатуры.ОсновнойПоставщик КАК ОсновнойПоставщик
ПОМЕСТИТЬ ВТ_АссортиментИПоставщики
ИЗ
ВТ_Номенклатура КАК ВТ_Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеПараметрыНоменклатуры.СрезПоследних(
&Момент,
ПодразделениеНазначения = &Подразделение
И Номенклатура В (ВЫБРАТЬ ВТ.Номенклатура ИЗ ВТ_Номенклатура КАК ВТ)
) КАК ПараметрыНоменклатуры
ПО ВТ_Номенклатура.Номенклатура = ПараметрыНоменклатуры.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОграничениеАссортиментаПодразделения.СрезПоследних(
&Момент,
ПодразделениеКомпании = &Подразделение
И Номенклатура В (ВЫБРАТЬ ВТ.Номенклатура ИЗ ВТ_Номенклатура КАК ВТ)
) КАК Ограничения
ПО ВТ_Номенклатура.Номенклатура = Ограничения.Номенклатура
ИНДЕКСИРОВАТЬ ПО
Номенклатура,
ОсновнойПоставщик
;
В финальной выборке связываем полученные данные с регистром цен поставщиков по полной паре ключевых измерений: Номенклатура и Контрагент. В конце пакета явно освобождаем системные ресурсы tempdb с помощью инструкции УНИЧТОЖИТЬ:
ВЫБРАТЬ
ВТ_Данные.Номенклатура КАК Товар,
ВТ_Данные.Состояние КАК Ассортимент,
ВТ_Данные.ОсновнойПоставщик КАК Поставщик,
ЦеныПоставщиков.Цена КАК Цена
ИЗ
ВТ_АссортиментИПоставщики КАК ВТ_Данные
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены.СрезПоследних(
&Момент,
(Контрагент, Номенклатура) В
(ВЫБРАТЬ
ВТ.ОсновнойПоставщик,
ВТ.Номенклатура
ИЗ
ВТ_АссортиментИПоставщики КАК ВТ)
) КАК ЦеныПоставщиков
ПО ВТ_Данные.ОсновнойПоставщик = ЦеныПоставщиков.Контрагент
И ВТ_Данные.Номенклатура = ЦеныПоставщиков.Номенклатура
;
УНИЧТОЖИТЬ ВТ_Номенклатура
;
УНИЧТОЖИТЬ ВТ_АссортиментИПоставщики
;
При оптимизации подобных запросов всегда придерживаемся следующих правил:
ПО и ГДЕ, если поле уже содержит нужную ссылку.ГДЕ или ПО всего запроса.