Почему зависает запрос к виртуальной таблице СрезПоследних при объединении и как его оптимизировать?

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

При разработке отчетов и обработок в 1С разработчики нередко сталкиваются с ситуацией, когда запрос, использующий объединение таблиц или виртуальные таблицы регистра сведений, зависает либо выполняется крайне медленно на рабочей базе, хотя на тестовой копии отрабатывает за доли секунды. Разберем типичные ошибки при построении таких запросов и рассмотрим практические способы их устранения.

Анализ проблемы: почему запрос зависает

Рассмотрим исходную ситуацию. В коде выполняется объединение двух выборок из одной и той же виртуальной таблицы СрезПоследних регистра сведений ЖурналУчетаБСО:


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

ОБЪЕДИНИТЬ ВСЕ

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

Проанализируем ключевые узкие места данного запроса:

  1. Обращение через точку к составному типу Регистратор в параметрах виртуальной таблицы. Поле Регистратор подчиненного регистра сведений почти всегда имеет составной тип. Конструкция Регистратор.МОЛотправитель заставляет платформу строить неявные левые соединения (LEFT JOIN) со всеми таблицами документов, являющихся регистраторами. Когда это происходит внутри параметров виртуальной таблицы СрезПоследних, СУБД (особенно Oracle и PostgreSQL) строит крайне неоптимальный план выполнения подзапроса с вычислением максимального периода, что приводит к полному зависанию.
  2. Фильтрация в секции ГДЕ вместо параметров виртуальной таблицы. В первой части запроса условие по МОЛ и статусу указано в секции ГДЕ. Это означает, что платформа сначала рассчитывает срез последних по всей таблице регистра без ограничений, и лишь затем отсекает лишние записи.
  3. Лишнее объединение однотипных данных. Запрос дважды обращается к одной и той же виртуальной таблице с одинаковой датой среза, создавая двойную нагрузку на СУБД.
  4. Разница между копией и рабочей базой. На рабочей базе сказываются накопленные объемы данных, фрагментация индексов, устаревшая статистика СУБД и транзакционные блокировки. Если запрос написан неоптимально, на маленькой копии он выполнится быстро за счет малого объема записей в памяти, а на рабочей базе вызовет деградацию производительности.

Способ 1. Перенос условий в параметры виртуальной таблицы и объединение условий

Если нам необходимо получить записи по одной дате среза, но с альтернативными условиями (по материально ответственному лицу или по отправителю в регистраторе), объединять две тяжелые виртуальные таблицы через ОБЪЕДИНИТЬ ВСЕ не требуется. Перенесем все условия непосредственно в параметры таблицы СрезПоследних.

Посмотрим на пример оптимизированного запроса:


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

Важные моменты этого варианта:

  1. Использование функции ВЫРАЗИТЬ(...) обязательно, если у поля Регистратор более одного типа. Это указывает платформе на соединение только с одной таблицей документа ДвижениеБСО вместо всех существующих регистраторов.
  2. Фильтрация внутри виртуальной таблицы позволяет СУБД применять индекс регистра еще до вычисления максимальных дат среза.

Способ 2. Предварительный отбор документов через временную таблицу

Если в регистре накоплен большой объем записей, обращение к реквизитам документов даже через ВЫРАЗИТЬ внутри параметров среза последних может приводить к снижению скорости. Наиболее надежный с точки зрения производительности подход — предварительно отобрать ссылки на документы во временную таблицу.

Разберем по шагам реализацию данного подхода:

  1. Выбираем документы ДвижениеБСО, удовлетворяющие условию по отправителю, в отдельную временную таблицу с индексацией по ссылке.
  2. Передаем список документов в условие виртуальной таблицы через оператор В.

// Шаг 1: Отбираем подходящие документы
ВЫБРАТЬ
    ДвижениеБСО.Ссылка КАК Ссылка
ПОМЕСТИТЬ вт_НужныеДокументы
ИЗ
    Документ.ДвижениеБСО КАК ДвижениеБСО
ГДЕ
    ДвижениеБСО.МОЛотправитель = &Контрагент
    И ДвижениеБСО.Проведен = ИСТИНА

ИНДЕКСИРОВАТЬ ПО
    Ссылка
;

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

Такой подход полностью исключает неявные соединения и сложные вычисления внутри подзапроса среза, обеспечивая стабильный и предсказуемый план запроса на любых СУБД.

Рекомендации по обслуживанию базы данных

Если после исправления текста запроса на рабочей базе все еще наблюдаются задержки, выполним следующие диагностические действия:

  1. Обновление статистики СУБД. Выполним регламентную процедуру обновления статистик для таблиц регистра и документов. Неактуальная статистика приводит к тому, что оптимизатор СУБД выбирает сканирование всей таблицы (табличный скан) вместо поиска по индексу.
  2. Анализ блокировок. Проверим с помощью консоли администрирования кластера 1С или монитора блокировок, не ожидает ли сеанс освобождения ресурсов, занятых проведением документов другими пользователями.
  3. Пересмотр структуры регистра. Если выборка по полю МОЛотправитель выполняется регулярно, имеет смысл добавить это поле непосредственно в состав измерений или реквизитов регистра сведений при проведении, чтобы не обращаться к шапке документа через регистратор.
← На главную