Почему отчет на СКД работает медленно при отборе по характеристикам в 1С?

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

В процессе эксплуатации старых систем, таких как 1С:Управление производственным предприятием 1.3 на платформе 8.2 в связке с MS SQL Server 2005, разработчики часто сталкиваются с внезапным падением производительности отчетов. Типичная ситуация: отчет, в основе которого лежит стандартный шаблон отчета на СКД с сохранением настроек, который годами работал быстро, начинает «зависать» при наложении отбора по дополнительным свойствам (характеристикам) в СКД. При этом использование обходных путей, таких как отбор через код (ВидПриемки.Код = "1"), дает мгновенный результат, в то время как прямой отбор по ссылке не выполняется вовсе.

В рамках данной статьи мы проанализируем причины такого поведения системы, разберем технические ошибки в структуре запросов и выясним, как помочь оптимизатору SQL Server строить эффективные планы выполнения.

Анализ проблемы: Ошибки планировщика и «занюхивание» параметров

Когда мы видим огромную разницу между Estimated Subtree Cost (предполагаемая стоимость) и Actual Duration (реальное время выполнения), это сигнализирует о том, что SQL Server ошибается в оценке количества строк (Cardinality Estimation). Для детального анализа таких ситуаций и отладки сложных пакетов данных рекомендуется использовать продвинутую консоль запросов, которая поддерживает работу с временными таблицами. Рассмотрим основные причины, почему это происходит:

  1. Parameter Sniffing (Переиспользование планов): SQL Server кэширует план запроса при первом выполнении. Если первый раз запрос был запущен с параметрами, под которые попадает мало данных, создается «легкий» план. Когда позже отчет запускается с «тяжелыми» параметрами, сервер пытается применить старый план, что приводит к катастрофическим задержкам.
  2. Отсутствие SARGability: Использование функций над полями индексов в условиях соединения. В представленном запросе мы видим конструкцию НАЧАЛОПЕРИОДА(Продажи.ДатаРеализации, ДЕНЬ). Применение функции НАЧАЛОПЕРИОДА к индексированной колонке делает невозможным операцию Index Seek. Сервер вынужден выполнять Index Scan (полный перебор), что при росте базы данных превращает запрос в «тыкву».
  3. Неявные соединения в СКД: Механизм характеристик СКД динамически добавляет LEFT JOIN к регистру сведений ЗначенияСвойствОбъектов. Если отбор идет по ссылке, оптимизатор может выбрать вложенные циклы (Nested Loops), которые крайне медленно работают на больших объемах данных. Обращение через .Код заставляет систему иначе строить дерево запроса, что часто подталкивает сервер к использованию более эффективных Hash Match соединений.

Оптимизация структуры запроса: Работа со ссылками вместо строк

Одной из критических ошибок в представленном коде является сравнение наименований свойств в тексте запроса. Рассмотрим подробнее этот фрагмент:


ГДЕ
    (ЗначенияСвойствОбъектов.Свойство.Наименование = "Тип продукции"
    ИЛИ ЗначенияСвойствОбъектов.Свойство.Наименование = "Корпус"
    ИЛИ ЗначенияСвойствОбъектов.Свойство.Наименование = "Тип исполнения")

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

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


СвойствоТипПродукции = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("Тип продукции");
Запрос.УстановитьПараметр("СвойствоТипПродукции", СвойствоТипПродукции);
// Аналогично для остальных свойств

После этого условие в запросе должно выглядеть так:


ГДЕ
    ЗначенияСвойствОбъектов.Свойство В (&СписокНужныхСвойств)

Переход от физических таблиц к виртуальным

Проанализируем вторую часть запроса, где идет обращение к таблице РегистрНакопления.Продажи. Использование физической таблицы напрямую в большинстве случаев является ошибкой, если вам нужны итоговые данные за период.

Рассмотрим разницу: При обращении к физической таблицы РегистрНакопления.Продажи система сканирует все записи, которые удовлетворяют условию ГДЕ. Если записей миллионы, поиск будет долгим. Виртуальная таблица Продажи.Обороты работает с предварительно рассчитанными итогами, что в десятки и сотни раз быстрее.

Перепишем фрагмент запроса с использованием виртуальной таблицы:


ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК Продажи

Важно помнить, что в виртуальных таблицах отборы нужно накладывать внутри параметров таблицы (в скобках), а не в секции ГДЕ. Это позволяет серверу БД получить уже отфильтрованный узкий набор данных.

Исправление логики соединений и работы с NULL

В исходном запросе используются сложные конструкции ВЫБОР КОГДА... для определения типов регистраторов и расчета стоимости. Рассмотрим, как это влияет на производительность. Большое количество условий ВЫБОР в выходных полях в сочетании с ЛЕВОЕ СОЕДИНЕНИЕ заставляет SQL Server выполнять дополнительные вычисления для каждой строки результата.

Обратите внимание на соединение с курсами валют:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
ПО (КурсыВалют.Валюта.Код = "840")
    И (НАЧАЛОПЕРИОДА(Продажи.ДатаРеализации, ДЕНЬ) = НАЧАЛОПЕРИОДА(КурсыВалют.Период, ДЕНЬ))

Как мы уже выяснили, использование НАЧАЛОПЕРИОДА убивает производительность. Выясним причину: сервер не может использовать индекс по колонке Период. Чтобы оптимизировать это, рекомендуется либо хранить в регистре продаж отдельную колонку ДатаБезВремени, либо передавать курсы валют в отдельную временную таблицу, где дата уже приведена к нужному формату без использования функций в условии ПО.

Использование временных таблиц для фильтрации

Для ускорения работы отчета, особенно в СКД, эффективно использовать цепочку временных таблиц. Такие подходы особенно актуальны, когда нужно обрабатывать сложные иерархии, например, если требуется сформировать дерево спецификаций быстро. Рассмотрим по шагам оптимальный алгоритм:

  1. Создаем временную таблицу ВТ_Объекты, в которую выбираем только те ссылки (Номенклатуру), которые соответствуют нашим отборам по характеристикам.
  2. Индексируем временную таблицу по полю Объект (Номенклатура).
  3. Во втором запросе пакета соединяем ВТ_Объекты с виртуальной таблицей Продажи.Обороты через ВНУТРЕННЕЕ СОЕДИНЕНИЕ.

Такой подход гарантирует, что основная выборка из регистра продаж будет ограничена только нужными товарами еще на уровне SQL-сервера, что резко снижает нагрузку на дисковую подсистему и память.

Рекомендации по обслуживанию SQL Server

Если программные методы оптимизации не помогают, следует обратить внимание на состояние базы данных. Для SQL Server 2005 и платформы 8.2 критически важны следующие действия:

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

← На главную