Как оптимизировать пакетный запрос к виртуальным таблицам среза последних в 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок в 1С часто требуется собрать срез данных сразу из нескольких периодических регистров сведений: цен, дополнительных реквизитов и ассортиментных ограничений. Неправильно составленный пакетный запрос может приводить к катастрофическому падению производительности на СУБД, вызывая избыточное сканирование таблиц, блокировки и разрастание временных таблиц.

Разберем исходный пример, проанализируем допущенные ошибки и по шагам построим оптимизированный вариант запроса.

Анализ проблем исходного запроса

Рассмотрим фрагмент исходного кода и выделим критические ошибки, влияющие на скорость выполнения:

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

Пошаговая оптимизация запроса

Разберем процесс исправления запроса шаг за шагом.

Шаг 1. Формирование базового списка номенклатуры с индексацией

Если требуется получить позиции номенклатуры, по которым установлены цены определенного типа, мы отбираем их и сразу индексируем поле связи. Обязательно используем РАЗЛИЧНЫЕ, чтобы избежать дублирования строк:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    Цены.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_Номенклатура
ИЗ
    РегистрСведений.Цены КАК Цены
ГДЕ
    Цены.ТипЦен = &ТипЦен
ИНДЕКСИРОВАТЬ ПО
    Номенклатура
;

Шаг 2. Передача отборов в виртуальные таблицы срезов

При соединении с регистрами сведений передаем список отобранной номенклатуры непосредственно в параметры виртуальных таблиц СрезПоследних. Это ограничит объем рассчитываемых итогов на стороне СУБД:


ВЫБРАТЬ
    ВТ_Номенклатура.Номенклатура КАК Номенклатура,
    Ограничения.Состояние КАК Состояние,
    ПараметрыНоменклатуры.ОсновнойПоставщик КАК ОсновнойПоставщик
ПОМЕСТИТЬ ВТ_АссортиментИПоставщики
ИЗ
    ВТ_Номенклатура КАК ВТ_Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеПараметрыНоменклатуры.СрезПоследних(
            &Момент,
            ПодразделениеНазначения = &Подразделение
                И Номенклатура В (ВЫБРАТЬ ВТ.Номенклатура ИЗ ВТ_Номенклатура КАК ВТ)
        ) КАК ПараметрыНоменклатуры
        ПО ВТ_Номенклатура.Номенклатура = ПараметрыНоменклатуры.Номенклатура
        
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ОграничениеАссортиментаПодразделения.СрезПоследних(
            &Момент,
            ПодразделениеКомпании = &Подразделение
                И Номенклатура В (ВЫБРАТЬ ВТ.Номенклатура ИЗ ВТ_Номенклатура КАК ВТ)
        ) КАК Ограничения
        ПО ВТ_Номенклатура.Номенклатура = Ограничения.Номенклатура
ИНДЕКСИРОВАТЬ ПО
    Номенклатура,
    ОсновнойПоставщик
;

Шаг 3. Корректное получение финальных данных и очистка ресурсов

В финальной выборке связываем полученные данные с регистром цен поставщиков по полной паре ключевых измерений: Номенклатура и Контрагент. В конце пакета явно освобождаем системные ресурсы tempdb с помощью инструкции УНИЧТОЖИТЬ:


ВЫБРАТЬ
    ВТ_Данные.Номенклатура КАК Товар,
    ВТ_Данные.Состояние КАК Ассортимент,
    ВТ_Данные.ОсновнойПоставщик КАК Поставщик,
    ЦеныПоставщиков.Цена КАК Цена
ИЗ
    ВТ_АссортиментИПоставщики КАК ВТ_Данные
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Цены.СрезПоследних(
            &Момент,
            (Контрагент, Номенклатура) В
                (ВЫБРАТЬ
                    ВТ.ОсновнойПоставщик,
                    ВТ.Номенклатура
                ИЗ
                    ВТ_АссортиментИПоставщики КАК ВТ)
        ) КАК ЦеныПоставщиков
        ПО ВТ_Данные.ОсновнойПоставщик = ЦеныПоставщиков.Контрагент
            И ВТ_Данные.Номенклатура = ЦеныПоставщиков.Номенклатура
;

УНИЧТОЖИТЬ ВТ_Номенклатура
;
УНИЧТОЖИТЬ ВТ_АссортиментИПоставщики
;

Ключевые выводы и рекомендации

При оптимизации подобных запросов всегда придерживаемся следующих правил:

  1. Никогда не обращаемся к реквизитам через точку в секциях ПО и ГДЕ, если поле уже содержит нужную ссылку.
  2. Всегда передаем условия фильтрации внутрь параметров виртуальных таблиц, а не выносим их в блок ГДЕ или ПО всего запроса.
  3. Индексируем поля временных таблиц, по которым планируется выполнять соединения в последующих пакетах.
  4. Контролируем полноту ключей соединения: при работе с периодическими регистрами связь должна выполняться по всем значимым измерениям, идентифицирующим запись.
  5. Уничтожаем временные таблицы в конце работы алгоритма для снижения нагрузки на дисковую подсистему СУБД.
← На главную