Как оптимизировать медленный запрос СКД при работе с документами и регистрами накопления?

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

При разработке отчетов на системе компоновки данных (СКД) программисты часто сталкиваются с ситуацией, когда запрос выполняется недопустимо долго — десятки минут даже за короткий период. Рассмотрим основные причины деградации производительности на примере конкретного запроса и разберем по шагам, как превратить «тяжелый» код в эффективный инструмент.

Анализ проблемных мест в исходном запросе

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

  1. Разыменование полей через точку (Dot-access): Обращения вида ЗаказПоставщикуТовары.Ссылка.Контрагент или ЗаказПоставщикуТовары.Ссылка.Дата заставляют СУБД делать неявные левые соединения с таблицей заголовков документов. Если таких полей много, количество соединений растет лавинообразно.
  2. Неправильное использование виртуальных таблиц: Обращение к РегистрНакопления.Закупки.Обороты без передачи параметров периода напрямую в таблицу заставляет систему сначала получить все данные из регистра, а затем фильтровать их в секции ГДЕ.
  3. Сложные условия в секции ПО (JOIN): Конструкция ПО (ВЫБОР КОГДА... ТОГДА... ИНАЧЕ... КОНЕЦ) убивает использование индексов. СУБД не может заранее построить оптимальный план выполнения и переходит к полному сканированию таблиц.
  4. Избыточность конструкций: Использование Ссылка.Ссылка не несет смысла и лишь перегружает текст запроса, а в редких случаях может сбивать оптимизатор.

Шаг 1. Оптимизация получения данных из документов через временные таблицы

Рассмотрим первый этап оптимизации. Вместо того чтобы обращаться к табличным частям и тянуть реквизиты «шапки» через точку, мы сначала подготовим данные. Выясним причину тормозов: при объединении Товары и Услуги с обращением к Ссылка.Контрагент система создает множество JOIN. Исправим это, используя временную таблицу.

Поместим данные о товарах и услугах в одну временную таблицу, предварительно «выпрямив» структуру. Посмотрим на пример кода:


ВЫБРАТЬ
    ЗаказПоставщикуТовары.Ссылка КАК ЗаказПоставщику,
    ЗаказПоставщикуТовары.Ссылка.Контрагент КАК Контрагент,
    ЗаказПоставщикуТовары.Номенклатура КАК Номенклатура,
    ЗаказПоставщикуТовары.Количество КАК Количество,
    ЗаказПоставщикуТовары.Сумма КАК Сумма,
    ЗаказПоставщикуТовары.Ссылка.ВалютаДокумента КАК Валюта,
    ЗаказПоставщикуТовары.Ссылка.КурсВзаиморасчетов КАК Курс
ПОМЕСТИТЬ втВсеТовары
ИЗ
    Документ.ЗаказПоставщику.Товары КАК ЗаказПоставщикуТовары
ГДЕ
    ЗаказПоставщикуТовары.Ссылка.Проведен = ИСТИНА
    И ЗаказПоставщикуТовары.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон

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

ВЫБРАТЬ
    ЗаказПоставщикуУслуги.Ссылка,
    ЗаказПоставщикуУслуги.Ссылка.Контрагент,
    ЗаказПоставщикуУслуги.Номенклатура,
    ЗаказПоставщикуУслуги.Количество,
    ЗаказПоставщикуУслуги.Сумма,
    ЗаказПоставщикуУслуги.Ссылка.ВалютаДокумента,
    ЗаказПоставщикуУслуги.Ссылка.КурсВзаиморасчетов
ИЗ
    Документ.ЗаказПоставщику.Услуги КАК ЗаказПоставщикуУслуги
ГДЕ
    ЗаказПоставщикуУслуги.Ссылка.Проведен = ИСТИНА
    И ЗаказПоставщикуУслуги.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон
;

Важно: Теперь у нас есть плоская таблица втВсеТовары. Проиндексируем ее по полю ЗаказПоставщику, так как далее мы будем соединяться по этому полю.

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

Проанализируем ситуацию с регистром Закупки.Обороты. В исходном запросе параметры периода были пустыми. Это значит, что 1С пытается прочитать весь регистр за все время существования базы! Чтобы исправить это, всегда передавайте &ДатаНач и &ДатаКон в параметры виртуальной таблицы.

Также обратите внимание на периодичность. Если вам нужна дата конкретного документа (регистратора), использование виртуальной таблицы оборотов с периодичностью Запись может быть медленнее, чем прямое чтение физической таблицы регистра накопления. Разберем, как подготовить данные из регистра отдельно:


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

Шаг 3. Устранение логики ВЫБОР в условиях соединения

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

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

Проанализируем пример разделения логики:


ВЫБРАТЬ
    Закупки.Регистратор,
    Закупки.Сделка КАК Заказ
ИЗ
    РегистрНакопления.Закупки КАК Закупки
ГДЕ
    НЕ Закупки.Регистратор ССЫЛКА Документ.Сторнирование

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

ВЫБРАТЬ
    Закупки.Регистратор,
    Закупки.Регистратор.ДокументОснование.Сделка
ИЗ
    РегистрНакопления.Закупки КАК Закупки
ГДЕ
    Закупки.Регистратор ССЫЛКА Документ.Сторнирование

Шаг 4. Использование оператора ВЫРАЗИТЬ

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

Пример: Вместо Закупки.Сделка = втТовары.Заказ напишем ВЫРАЗИТЬ(Закупки.Сделка КАК Документ.ЗаказПоставщику) = втТовары.Заказ. Это позволит системе обращаться напрямую к таблице заказов поставщикам, исключая лишние проверки.

Итоговые рекомендации по структуре запроса

Соберем воедино все рекомендации для достижения максимальной скорости работы отчета:

  1. Используйте временные таблицы: Сначала выберите данные из первичных документов во временную таблицу, сразу вычисляя нужные значения (например, стоимость в валюте).
  2. Индексируйте поля связи: Все поля, которые участвуют в условии ПО при соединении временных таблиц, должны быть указаны в секции ИНДЕКСИРОВАТЬ ПО.
  3. Ограничивайте выборку: Параметры периода должны попадать в виртуальные таблицы регистров максимально «глубоко».
  4. Избегайте «точек» в финальном запросе: В конечном селекте старайтесь выбирать поля только из уже подготовленных временных таблиц.
  5. Группировка: Если вам нужны итоги, делайте группировку на этапе подготовки временных таблиц, чтобы уменьшить объем данных для финального соединения.

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

← На главную