Как оптимизировать запрос отчета по остаткам и стоимости товаров, чтобы избежать ошибки «Недостаточно памяти»?

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

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

Анализ основной причины низкой производительности

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

Почему это плохо? Виртуальная таблица ОстаткиИОбороты сама по себе является сложным вложенным запросом, который СУБД должна вычислить «на лету». Когда мы соединяем три такие таблицы, оптимизатор СУБД часто не может построить эффективный план выполнения. В результате создается огромное промежуточное произведение записей (Cartesian product в памяти), что и приводит к ошибке «Недостаточно памяти».

Кроме того, в исходном запросе были допущены следующие ошибки:

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

Шаг 1: Перенос фильтров в параметры виртуальных таблиц

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

Вместо:


ИЗ РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаНач, &ДатаКон, Месяц, , ) КАК Таблица
{ГДЕ Таблица.Склад.*}

Используем:


ИЗ РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаНач, &ДатаКон, Месяц, , {Склад.*, Номенклатура.*}) КАК Таблица

Шаг 2: Использование конструкции ОБЪЕДИНИТЬ ВСЕ вместо СОЕДИНЕНИЯ

Выясним, как принципиально изменить архитектуру запроса. Вместо того чтобы пытаться «склеить» таблицы по горизонтали (через ЛЕВОЕ СОЕДИНЕНИЕ), мы получим данные из каждого регистра по отдельности и сложим их по вертикали с помощью ОБЪЕДИНИТЬ ВСЕ. Затем мы сгруппируем результат по измерениям.

Этот метод считается «золотым стандартом» разработки тяжелых отчетов в 1С. Рассмотрим пример реализации такой структуры:


ВЫБРАТЬ
    Т.Склад КАК Склад,
    Т.Номенклатура КАК Номенклатура,
    Т.ХарактеристикаНоменклатуры КАК Характеристика,
    Т.Период КАК Период,
    Т.КоличествоНачальныйОстаток КАК КоличествоНачальныйОстаток,
    Т.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток,
    0 КАК СтоимостьНачальныйОстаток,
    0 КАК СтоимостьКонечныйОстаток
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&ДатаНач, &ДатаКон, Месяц, , {Склад.*, Номенклатура.*}) КАК Т

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

ВЫБРАТЬ
    П.Склад,
    П.Номенклатура,
    П.ХарактеристикаНоменклатуры,
    П.Период,
    0,
    0,
    П.СтоимостьНачальныйОстаток,
    П.СтоимостьКонечныйОстаток
ИЗ
    РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(&ДатаНач, &ДатаКон, Месяц, , {Склад.*, Номенклатура.*}) КАК П
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Д.Склад,
    Д.Номенклатура,
    Д.Характеристика,
    НАЧАЛОПЕРИОДА(Д.Период, МЕСЯЦ) КАК ПериодМесяц,
    СУММА(Д.КоличествоНачальныйОстаток) КАК КоличествоНачальныйОстаток,
    СУММА(Д.КоличествоКонечныйОстаток) КАК КоличествоКонечныйОстаток,
    СУММА(Д.СтоимостьНачальныйОстаток) КАК СтоимостьНачальныйОстаток,
    СУММА(Д.СтоимостьКонечныйОстаток) КАК СтоимостьКонечныйОстаток
ИЗ
    ВТ_Данные КАК Д
СГРУППИРОВАТЬ ПО
    Д.Склад,
    Д.Номенклатура,
    Д.Характеристика,
    НАЧАЛОПЕРИОДА(Д.Период, МЕСЯЦ)

Шаг 3: Работа с временными таблицами и индексацией

Проанализируем ситуацию, когда данных очень много. Если мы просто объединяем таблицы, СУБД все равно может испытывать трудности при итоговой группировке. Чтобы помочь системе, разделим получение данных на этапы с использованием временных таблиц и обязательной индексацией.

Важный момент: Если вы используете временные таблицы (ПОМЕСТИТЬ), обязательно добавляйте ИНДЕКСИРОВАТЬ ПО для тех полей, по которым в дальнейшем будет происходить группировка или связь. Это критично для производительности на больших объемах данных.

Разберем по шагам создание пакета запросов:

  1. Создаем ВТ_Количество, выбирая данные из ТоварыНаСкладах. Индексируем по полям Склад, Номенклатура, Характеристика.
  2. Создаем ВТ_Стоимость, выбирая данные из ПартииТоваровНаСкладах. Также индексируем.
  3. В финальном запросе объединяем данные из этих ВТ и выполняем итоговую агрегацию СУММА(...).

Шаг 4: Учет характеристик и серий

В конфигурациях типа УПП данные в регистре остатков и регистре партий могут расходиться, если не ведется строгий партионный учет. Однако для корректного сопоставления крайне важно соединять или группировать данные по всем измерениям. Если вы проигнорируете ХарактеристикаНоменклатуры, ваш отчет покажет «кашу» из цифр.

Посмотрим на пример правильной группировки в итоговом запросе:


СГРУППИРОВАТЬ ПО
    ВТ_Данные.Склад,
    ВТ_Данные.Номенклатура,
    ВТ_Данные.ХарактеристикаНоменклатуры,
    ВТ_Данные.Качество,
    НАЧАЛОПЕРИОДА(ВТ_Данные.Период, МЕСЯЦ)

Рекомендации по работе с Универсальным отчетом

Поскольку вы строите отчет на базе Универсального отчета (или СКД), помните о настройках итогов. Универсальный отчет автоматически добавляет свои секции итогов, что может дублироваться с вашим кодом запроса. Проанализируйте текст запроса, который генерирует система (в консоли запросов), чтобы убедиться, что автоматические ИТОГИ не создают лишнюю нагрузку.

Если отчет по-прежнему работает медленно, рассмотрите возможность разделения его на два разных отчета: один только по количеству (по оперативному регистру), другой — стоимостной анализ. Часто пользователям не нужен полный разворот ежесекундно, и разделение задач позволяет системе работать стабильнее.

Итог: Основной секрет оптимизации — отказ от соединений виртуальных таблиц в пользу объединения (UNION ALL) и перенос всех условий в параметры этих таблиц. Это снижает нагрузку на оперативную память сервера в десятки раз и позволяет формировать отчеты за длительные периоды без ошибок.

← На главную