Медленная работа запросов — одна из наиболее распространенных проблем при разработке на платформе 1С. Часто программисты сталкиваются с ситуацией, когда запрос корректно выдает данные, но время его выполнения измеряется минутами. В этой статье мы подробно проанализируем типичный сложный запрос, найдем "узкие места" и разберем методы оптимизации, которые позволят ускорить выполнение кода в десятки раз.
Рассмотрим ситуацию, когда в запросе соединяются данные по остаткам, резервам и заказам покупателей. Основные причины падения производительности в таких конструкциях обычно кроются в неэффективном использовании виртуальных таблиц, обращении к полям через точку и неправильной работе с оператором В ИЕРАРХИИ. Давайте разберем по шагам, что именно нужно исправить.
Это классическая ошибка. Проанализируем фрагмент запроса:
ИЗ РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(, &СегодняшняяДата, Авто, , ) КАК ТоварыНаСкладахОстаткиИОбороты
...
ГДЕ ТоварыНаСкладахОстаткиИОбороты.Качество = &Качество И ТоварыНаСкладахОстаткиИОбороты.Склад = &Склад
Когда условие отбора (например, по складу или качеству) стоит в секции ГДЕ, система сначала выбирает все остатки и обороты по всем складам организации из базы данных, и только потом фильтрует их в памяти сервера 1С. Чтобы оптимизировать этот узел, необходимо передавать условия напрямую в параметры виртуальной таблицы.
Как сделать правильно: передадим параметры в первую скобку виртуальной таблицы. Это позволит СУБД выполнить фильтрацию еще на этапе чтения данных с диска.
ИЗ РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(, &СегодняшняяДата, Авто, , Склад = &Склад И Качество = &Качество) КАК ТоварыНаСкладахОстаткиИОбороты
В тексте часто встречается конструкция ЗаказПокупателяТовары.Ссылка.ДатаОтгрузки. На уровне SQL это приводит к неявному левому соединению (LEFT JOIN) таблицы табличной части с таблицей шапки документа. Если в базе сотни тысяч строк в заказах, это создает колоссальную нагрузку.
Рассмотрим, как это оптимизировать:
Такой подход позволяет избежать многократного обращения к таблице шапки документа для каждой строки товаров.
Оператор В ИЕРАРХИИ — один из самых ресурсоемких. В SQL он разворачивается в сложную структуру с подзапросами. Если вы используете его несколько раз в разных частях пакета запросов, производительность будет падать лавинообразно.
Рекомендуемый подход:
ВТ_Номенклатура.ГДЕ Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_Номенклатура).Когда мы создаем временные таблицы (например, ВТ_Номенклатура или Номенклатура_ВЗаказах) и в дальнейшем используем их в соединениях по полю Номенклатура, система должна быстро находить соответствующие записи. Без индекса СУБД будет выполнять полный перебор таблицы при каждом соединении.
Важное правило: индексируйте поля временных таблиц, по которым идет связь (ПО) или условие. В конструкторе запросов это делается на закладке "Индексы".
ВЫБРАТЬ Номенклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_Номенклатура
ИЗ Справочник.Номенклатура КАК Номенклатура
ГДЕ Номенклатура.Ссылка В ИЕРАРХИИ(&ГруппаНоменклатуры)
ИНДЕКСИРОВАТЬ ПО Номенклатура;
Обратим внимание на финальное соединение. Если вы используете ЛЕВОЕ СОЕДИНЕНИЕ, но в секции ГДЕ ставите условие на поле из "правой" таблицы, соединение фактически превращается во ВНУТРЕННЕЕ. Это происходит потому, что для отсутствующих записей поля будут иметь значение NULL, а сравнение NULL = &Параметр всегда ложно.
Чтобы сохранить логику левого соединения и не терять данные, условия по правой таблице (например, склад или качество) нужно переносить либо в параметры виртуальной таблицы, либо непосредственно в условие связи ПО.
В запросе часто используется таблица ОстаткиИОбороты, хотя фактически разработчику нужен только КонечныйОстаток. Виртуальная таблица ОстаткиИОбороты самая "тяжелая", так как она рассчитывает итоги и движения одновременно.
Выясним причину: если вам не нужны обороты за период, всегда используйте таблицу .Остатки(). Она работает значительно быстрее, так как обращается к заранее рассчитанным итогам на начало месяца и прибавляет к ним только движения за текущий период.
Иногда в запросах встречаются конструкции типа ЕСТЬNULL(0, 0) или 0 КАК Поле1, которые остаются после отладки или планируются "на будущее". Несмотря на то, что современные оптимизаторы SQL могут их игнорировать, они усложняют чтение запроса и построение плана выполнения. Проанализируем ситуацию: лучше сразу передавать готовые данные или использовать расчет внутри запроса без лишних вложенных ЕСТЬNULL.
Разберем пример очистки выражения:
Вместо: ЕСТЬNULL(Поле1, 0) - ЕСТЬNULL(Поле2, 0) + ЕСТЬNULL(0, 0)
Используем: ЕСТЬNULL(Поле1, 0) - ЕСТЬNULL(Поле2, 0)
Применяя эти правила, можно добиться стабильной и быстрой работы даже самых сложных отчетов в 1С.