Почему зависает запрос 1С при выполнении и как его оптимизировать

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

Зависание программы при выполнении запроса — одна из самых распространенных проблем, с которой сталкиваются разработчики 1С. Часто это происходит не из-за объема данных, а из-за того, как написан текст запроса. В этой статье мы проанализируем ситуацию, когда запрос к документам РеализацияТоваровУслуг и ОтчетПроизводстваЗаСмену приводит к критическому падению производительности, и разберем пошагово, как это исправить.

Выясним причину: почему запрос работает медленно?

Рассмотрим исходный запрос, который «вешает» систему. В нем можно выделить сразу несколько «антипаттернов» разработки:

  1. Обращение через точку к полям составного типа. Поля Сделка и ЗаказРезерв имеют составной тип (могут содержать ссылки на разные документы). Когда мы пишем Сделка.Дата, платформа 1С на уровне SQL выполняет левое соединение со всеми таблицами, входящими в этот тип. Если типов много, количество соединений в плане SQL-запроса становится огромным.
  2. Использование ВЫБОР (CASE) в секции ГДЕ. Это заставляет СУБД выполнять сканирование всей таблицы (Full Scan), так как оптимизатор не может заранее определить, какой индекс использовать.
  3. Вложенные запросы в условии В. Вложенные конструкции в условиях фильтрации часто превращаются в зависимые подзапросы, которые выполняются для каждой строки основной таблицы.
  4. Функция ДОБАВИТЬКДАТЕ в теле запроса. Любые вычисления в запросе над параметрами или полями мешают SQL-серверу эффективно строить план выполнения.

Шаг 1: Правильная работа с составными типами

Проанализируем ситуацию с полем Сделка. Если нам заранее известно, что нас интересуют только документы типа ЗаказПокупателя, мы должны явно «подсказать» это платформе. Для этого используется оператор ВЫРАЗИТЬ.

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


ВЫБРАТЬ
    ВЫРАЗИТЬ(РеализацияТоваровУслуг.Сделка КАК Документ.ЗаказПокупателя) КАК Заказ
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
    ВЫРАЗИТЬ(РеализацияТоваровУслуг.Сделка КАК Документ.ЗаказПокупателя).Дата > &НачалоПериода

Шаг 2: Использование временных таблиц вместо вложенных запросов

Разберем, как ускорить выборку данных. Вместо того чтобы городить сложные конструкции с ВЫБОР и вложенными запросами в ГДЕ, рассмотрим стратегию разделения задачи на этапы с использованием временных таблиц. Это позволит SQL-серверу проиндексировать промежуточные результаты.

Сначала соберем все подходящие заказы из документов реализации во временную таблицу:


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

Обратите внимание, что мы заранее рассчитали дату &ДатаНачалаДляЗаказов в коде на языке 1С, избавившись от функции ДОБАВИТЬКДАТЕ внутри запроса.

Шаг 3: Оптимизация логики выбора (ИЛИ против ОБЪЕДИНИТЬ)

В исходном запросе была логика: если параметр &ЗаказПокупателя пустой — выбираем по периоду, если заполнен — выбираем конкретный заказ. Использование ВЫБОР в ГДЕ губительно. Проанализируем лучший способ решения этой задачи.

Рекомендуется разбить выполнение на два условия в программном коде. Это самый быстрый вариант, так как SQL получит максимально простой и понятный текст запроса:


Если ЗначениеЗаполнено(ЗаказПокупателя) Тогда
    // Если заказ выбран, нам вообще не нужны сложные расчеты и временные таблицы
    ТекстЗапроса = "ВЫБРАТЬ &ЗаказПокупателя КАК Заказ";
Иначе
    // Если заказ не выбран, выполняем основной оптимизированный запрос
    ТекстЗапроса = "... (текст с временными таблицами) ...";
КонецЕсли;

Шаг 4: Избавляемся от лишних соединений через .Ссылка

Важный совет: никогда не используйте Документ.Ссылка.Поле, если вы уже находитесь в контексте этой таблицы. Например, РеализацияТоваровУслуг.Ссылка.Дата — это избыточное соединение таблицы самой с собой. Используйте просто РеализацияТоваровУслуг.Дата.

Итоговый оптимизированный пример запроса

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


// Подготовим дату заранее в коде 1С
ДатаПрошлогоГода = ДобавитьМесяц(НачалоПериода, -12);

Запрос.УстановитьПараметр("НачалоПериода", НачалоПериода);
Запрос.УстановитьПараметр("ДатаПрошлогоГода", ДатаПрошлогоГода);
Запрос.УстановитьПараметр("ЗаказПокупателя", ЗаказПокупателя);

// Текст запроса
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВЫРАЗИТЬ(РеализацияТоваровУслуг.Сделка КАК Документ.ЗаказПокупателя) КАК Заказ
ПОМЕСТИТЬ втВсеЗаказы
ИЗ
    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
    РеализацияТоваровУслуг.Дата > &НачалоПериода
    И ВЫРАЗИТЬ(РеализацияТоваровУслуг.Сделка КАК Документ.ЗаказПокупателя).Дата МЕЖДУ &ДатаПрошлогоГода И &НачалоПериода

ОБЪЕДИНИТЬ

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    втВсеЗаказы.Заказ
ИЗ
    втВсеЗаказы КАК втВсеЗаказы
ГДЕ
    (&ЗаказПокупателя = ЗНАЧЕНИЕ(Документ.ЗаказПокупателя.ПустаяСсылка)
        ИЛИ втВсеЗаказы.Заказ = &ЗаказПокупателя)
СГРУППИРОВАТЬ ПО
    втВсеЗаказы.Заказ

Подведем итоги

Чтобы ваш запрос никогда не приводил к зависанию системы, придерживайтесь следующих правил:

Следуя этим рекомендациям, вы обеспечите стабильную и быструю работу ваших отчетов и обработок даже на очень больших базах данных.

← На главную