В процессе эксплуатации старых систем, таких как 1С:Управление производственным предприятием 1.3 на платформе 8.2 в связке с MS SQL Server 2005, разработчики часто сталкиваются с внезапным падением производительности отчетов. Типичная ситуация: отчет, в основе которого лежит стандартный шаблон отчета на СКД с сохранением настроек, который годами работал быстро, начинает «зависать» при наложении отбора по дополнительным свойствам (характеристикам) в СКД. При этом использование обходных путей, таких как отбор через код (ВидПриемки.Код = "1"), дает мгновенный результат, в то время как прямой отбор по ссылке не выполняется вовсе.
В рамках данной статьи мы проанализируем причины такого поведения системы, разберем технические ошибки в структуре запросов и выясним, как помочь оптимизатору SQL Server строить эффективные планы выполнения.
Когда мы видим огромную разницу между Estimated Subtree Cost (предполагаемая стоимость) и Actual Duration (реальное время выполнения), это сигнализирует о том, что SQL Server ошибается в оценке количества строк (Cardinality Estimation). Для детального анализа таких ситуаций и отладки сложных пакетов данных рекомендуется использовать продвинутую консоль запросов, которая поддерживает работу с временными таблицами. Рассмотрим основные причины, почему это происходит:
НАЧАЛОПЕРИОДА(Продажи.ДатаРеализации, ДЕНЬ). Применение функции НАЧАЛОПЕРИОДА к индексированной колонке делает невозможным операцию Index Seek. Сервер вынужден выполнять Index Scan (полный перебор), что при росте базы данных превращает запрос в «тыкву».LEFT JOIN к регистру сведений ЗначенияСвойствОбъектов. Если отбор идет по ссылке, оптимизатор может выбрать вложенные циклы (Nested Loops), которые крайне медленно работают на больших объемах данных. Обращение через .Код заставляет систему иначе строить дерево запроса, что часто подталкивает сервер к использованию более эффективных Hash Match соединений.Одной из критических ошибок в представленном коде является сравнение наименований свойств в тексте запроса. Рассмотрим подробнее этот фрагмент:
ГДЕ
(ЗначенияСвойствОбъектов.Свойство.Наименование = "Тип продукции"
ИЛИ ЗначенияСвойствОбъектов.Свойство.Наименование = "Корпус"
ИЛИ ЗначенияСвойствОбъектов.Свойство.Наименование = "Тип исполнения")
Почему это плохо: Платформа 1С гораздо эффективнее работает со ссылочными типами данных. Сравнение строк — это медленная операция, которая к тому же не позволяет эффективно использовать индексы по полю Свойство. Разберем, как правильно переписать это условие.
Нам необходимо заранее получить ссылки на нужные свойства и передать их в запрос как параметры. Проанализируем пример кода на языке 1С для подготовки параметров:
СвойствоТипПродукции = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("Тип продукции");
Запрос.УстановитьПараметр("СвойствоТипПродукции", СвойствоТипПродукции);
// Аналогично для остальных свойств
После этого условие в запросе должно выглядеть так:
ГДЕ
ЗначенияСвойствОбъектов.Свойство В (&СписокНужныхСвойств)
Проанализируем вторую часть запроса, где идет обращение к таблице РегистрНакопления.Продажи. Использование физической таблицы напрямую в большинстве случаев является ошибкой, если вам нужны итоговые данные за период.
Рассмотрим разницу: При обращении к физической таблицы РегистрНакопления.Продажи система сканирует все записи, которые удовлетворяют условию ГДЕ. Если записей миллионы, поиск будет долгим. Виртуальная таблица Продажи.Обороты работает с предварительно рассчитанными итогами, что в десятки и сотни раз быстрее.
Перепишем фрагмент запроса с использованием виртуальной таблицы:
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК Продажи
Важно помнить, что в виртуальных таблицах отборы нужно накладывать внутри параметров таблицы (в скобках), а не в секции ГДЕ. Это позволяет серверу БД получить уже отфильтрованный узкий набор данных.
В исходном запросе используются сложные конструкции ВЫБОР КОГДА... для определения типов регистраторов и расчета стоимости. Рассмотрим, как это влияет на производительность. Большое количество условий ВЫБОР в выходных полях в сочетании с ЛЕВОЕ СОЕДИНЕНИЕ заставляет SQL Server выполнять дополнительные вычисления для каждой строки результата.
Обратите внимание на соединение с курсами валют:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
ПО (КурсыВалют.Валюта.Код = "840")
И (НАЧАЛОПЕРИОДА(Продажи.ДатаРеализации, ДЕНЬ) = НАЧАЛОПЕРИОДА(КурсыВалют.Период, ДЕНЬ))
Как мы уже выяснили, использование НАЧАЛОПЕРИОДА убивает производительность. Выясним причину: сервер не может использовать индекс по колонке Период. Чтобы оптимизировать это, рекомендуется либо хранить в регистре продаж отдельную колонку ДатаБезВремени, либо передавать курсы валют в отдельную временную таблицу, где дата уже приведена к нужному формату без использования функций в условии ПО.
Для ускорения работы отчета, особенно в СКД, эффективно использовать цепочку временных таблиц. Такие подходы особенно актуальны, когда нужно обрабатывать сложные иерархии, например, если требуется сформировать дерево спецификаций быстро. Рассмотрим по шагам оптимальный алгоритм:
ВТ_Объекты, в которую выбираем только те ссылки (Номенклатуру), которые соответствуют нашим отборам по характеристикам.Объект (Номенклатура).ВТ_Объекты с виртуальной таблицей Продажи.Обороты через ВНУТРЕННЕЕ СОЕДИНЕНИЕ.Такой подход гарантирует, что основная выборка из регистра продаж будет ограничена только нужными товарами еще на уровне SQL-сервера, что резко снижает нагрузку на дисковую подсистему и память.
Если программные методы оптимизации не помогают, следует обратить внимание на состояние базы данных. Для SQL Server 2005 и платформы 8.2 критически важны следующие действия:
UPDATE STATISTICS [ИмяТаблицы] WITH FULLSCAN. Стандартное ночное обновление может использовать лишь частичную выборку данных, что приводит к неверным планам.DBCC FREEPROCCACHE. Это заставит сервер перестроить все планы запросов. Если отчет заработал быстро — проблема была в Parameter Sniffing.Index Seek значительно возрастает. Используйте ALTER INDEX ALL ON [Таблица] REBUILD.Подводя итог, отметим: «магическое» ускорение запроса при использовании .Код — это лишь следствие того, что оптимизатор смог построить другой план. Правильное же решение заключается в отказе от функций в условиях, переходе на виртуальные таблицы и использовании типизированных параметров вместо строковых констант.