Как в отчете СКД разделить продажи и возвраты и избежать ошибки нехватки памяти?

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

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

Причины возникновения ошибки «Недостаток памяти»

Проанализируем типовые ошибки в исходном запросе, которые приводят к перегрузке оперативной памяти сервера или клиента:

  1. Разыменование составного ссылочного поля в секции условий. Поле Регистратор в регистрах накопления имеет составной тип, включающий множество видов документов. Обращение к его полям через точку вида Регистратор.Дата заставляет СУБД выполнять неявные левые соединения (LEFT JOIN) со всеми возможными таблицами документов-регистраторов этой конфигурации. Это многократно утяжеляет план выполнения запроса.
  2. Отсутствие параметров периода в виртуальной таблице. Если фильтр по дате наложен через условие ГДЕ, а параметры периода в самой виртуальной таблице Обороты оставлены пустыми, платформа 1С пытается рассчитать итоги за весь период ведения информационной базы, а затем накладывает фильтрацию в оперативной памяти.
  3. Избыточная детализация виртуальной таблицы. Выбор поля Регистратор отключает использование предрассчитанных итогов регистра накопления, заставляя систему сканировать физические таблицы движений.

Шаг 1. Разделение отгрузок и возвратов через конструкцию ВЫБОР

Чтобы получить отдельные колонки для продаж и возвратов в одном наборе данных, используем условную конструкцию ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ 0 КОНЕЦ. Проверять тип документа надежнее и быстрее с помощью логического оператора ССЫЛКА, а не через функцию ТИПЗНАЧЕНИЯ, так как оператор ССЫЛКА транслируется на уровне СУБД в быструю проверку внешнего ключа без лишних преобразований типов.

Посмотрим на пример текста запроса для набора данных СКД:


ВЫБРАТЬ
	ПродажиОбороты.Контрагент КАК Контрагент,
	ПродажиОбороты.Договор КАК Договор,
	ПродажиОбороты.Номенклатура КАК Номенклатура,
	ПродажиОбороты.Регистратор КАК Регистратор,
	ВЫБОР
		КОГДА ПродажиОбороты.Регистратор ССЫЛКА Документ.ОтгрузкаПокупателю
			ТОГДА ПродажиОбороты.КоличествоОборот
		ИНАЧЕ 0
	КОНЕЦ КАК КоличествоПродажи,
	ВЫБОР
		КОГДА ПродажиОбороты.Регистратор ССЫЛКА Документ.ОтгрузкаПокупателю
			ТОГДА ПродажиОбороты.СуммаОборот
		ИНАЧЕ 0
	КОНЕЦ КАК СуммаПродажи,
	ВЫБОР
		КОГДА ПродажиОбороты.Регистратор ССЫЛКА Документ.ВозвратОтПокупателя
			ТОГДА -ПродажиОбороты.КоличествоОборот
		ИНАЧЕ 0
	КОНЕЦ КАК КоличествоВозврата,
	ВЫБОР
		КОГДА ПродажиОбороты.Регистратор ССЫЛКА Документ.ВозвратОтПокупателя
			ТОГДА -ПродажиОбороты.СуммаОборот
		ИНАЧЕ 0
	КОНЕЦ КАК СуммаВозврата
ИЗ
	РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК ПродажиОбороты
ГДЕ
	ПродажиОбороты.Номенклатура.ТипНоменклатурыБухгалтерии = &ТипНоменклатурыБухгалтерии

Обратите внимание на знак минус перед показателями возврата: в типовых регистрах накопления движения по возврату часто записываются со знаком минус либо со знаком плюс в зависимости от логики регистра. Если возврат уменьшает оборот и хранится с отрицательным знаком, взятие обратного значения позволяет получить положительное число для отдельной колонки возвратов.

Шаг 2. Оптимизация обращения к периоду

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

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


ВЫБРАТЬ
	Продажи.Контрагент КАК Контрагент,
	Продажи.Номенклатура КАК Номенклатура,
	Продажи.Регистратор КАК Регистратор,
	ВЫБОР
		КОГДА Продажи.Регистратор ССЫЛКА Документ.ОтгрузкаПокупателю
			ТОГДА Продажи.Количество
		ИНАЧЕ 0
	КОНЕЦ КАК КоличествоПродажи,
	ВЫБОР
		КОГДА Продажи.Регистратор ССЫЛКА Документ.ВозвратОтПокупателя
			ТОГДА Продажи.Количество
		ИНАЧЕ 0
	КОНЕЦ КАК КоличествоВозврата
ИЗ
	РегистрНакопления.Продажи КАК Продажи
ГДЕ
	Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода

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

Шаг 3. Корректный расчет процента возвратов в СКД

Частая ошибка разработчиков — попытка рассчитать процент возврата на вкладке «Вычисляемые поля» по формуле:

100 * (КоличествоВозврата / КоличествоПродажи)

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

Правильный расчет процентов и коэффициентов в СКД настраивается исключительно на вкладке «Ресурсы» с предварительным агрегированием слагаемых через функцию Сумма():


ВЫБОР 
	КОГДА ЕстьNull(Сумма(КоличествоПродажи), 0) = 0 
		ТОГДА 0 
	ИНАЧЕ 100 * (Сумма(КоличествоВозврата) / Сумма(КоличествоПродажи)) 
КОНЕЦ

При такой настройке система компоновки данных сначала просуммирует количество продаж и возвратов по всей выбранной группировке (например, по номенклатуре, контрагенту или в целом по отчету) и только затем вычислит корректный процент возврата.

← На главную