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