При построении отчетов по продажам в системе компоновки данных (СКД) часто возникает задача вывести показатели отгрузки покупателям и возвратов от них в отдельные колонки, а также посчитать производный показатель — процент возврата. Однако при некорректном составлении запроса к регистру накопления можно столкнуться с падением производительности, ошибкой «Недостаток памяти» и некорректными результатами вычисляемых полей. Разберем по шагам, почему возникают эти проблемы и как построить оптимальный отчет.
Проанализируем типовые ошибки в исходном запросе, которые приводят к перегрузке оперативной памяти сервера или клиента:
Регистратор в регистрах накопления имеет составной тип, включающий множество видов документов. Обращение к его полям через точку вида Регистратор.Дата заставляет СУБД выполнять неявные левые соединения (LEFT JOIN) со всеми возможными таблицами документов-регистраторов этой конфигурации. Это многократно утяжеляет план выполнения запроса.ГДЕ, а параметры периода в самой виртуальной таблице Обороты оставлены пустыми, платформа 1С пытается рассчитать итоги за весь период ведения информационной базы, а затем накладывает фильтрацию в оперативной памяти.Регистратор отключает использование предрассчитанных итогов регистра накопления, заставляя систему сканировать физические таблицы движений.Чтобы получить отдельные колонки для продаж и возвратов в одном наборе данных, используем условную конструкцию ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ 0 КОНЕЦ. Проверять тип документа надежнее и быстрее с помощью логического оператора ССЫЛКА, а не через функцию ТИПЗНАЧЕНИЯ, так как оператор ССЫЛКА транслируется на уровне СУБД в быструю проверку внешнего ключа без лишних преобразований типов.
Посмотрим на пример текста запроса для набора данных СКД:
ВЫБРАТЬ
ПродажиОбороты.Контрагент КАК Контрагент,
ПродажиОбороты.Договор КАК Договор,
ПродажиОбороты.Номенклатура КАК Номенклатура,
ПродажиОбороты.Регистратор КАК Регистратор,
ВЫБОР
КОГДА ПродажиОбороты.Регистратор ССЫЛКА Документ.ОтгрузкаПокупателю
ТОГДА ПродажиОбороты.КоличествоОборот
ИНАЧЕ 0
КОНЕЦ КАК КоличествоПродажи,
ВЫБОР
КОГДА ПродажиОбороты.Регистратор ССЫЛКА Документ.ОтгрузкаПокупателю
ТОГДА ПродажиОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ КАК СуммаПродажи,
ВЫБОР
КОГДА ПродажиОбороты.Регистратор ССЫЛКА Документ.ВозвратОтПокупателя
ТОГДА -ПродажиОбороты.КоличествоОборот
ИНАЧЕ 0
КОНЕЦ КАК КоличествоВозврата,
ВЫБОР
КОГДА ПродажиОбороты.Регистратор ССЫЛКА Документ.ВозвратОтПокупателя
ТОГДА -ПродажиОбороты.СуммаОборот
ИНАЧЕ 0
КОНЕЦ КАК СуммаВозврата
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК ПродажиОбороты
ГДЕ
ПродажиОбороты.Номенклатура.ТипНоменклатурыБухгалтерии = &ТипНоменклатурыБухгалтерии
Обратите внимание на знак минус перед показателями возврата: в типовых регистрах накопления движения по возврату часто записываются со знаком минус либо со знаком плюс в зависимости от логики регистра. Если возврат уменьшает оборот и хранится с отрицательным знаком, взятие обратного значения позволяет получить положительное число для отдельной колонки возвратов.
Выясним причину, почему нельзя фильтровать даты через условие ГДЕ. Период обязательно должен передаваться в параметры самой виртуальной таблицы: РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Авто, ...).
Если для отчета критически необходима детальная информация по каждому отдельному документу и агрегация по периодам не требуется, более эффективным решением может стать обращение к реальной таблице движений РегистрНакопления.Продажи вместо таблицы оборотов:
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
Продажи.Номенклатура КАК Номенклатура,
Продажи.Регистратор КАК Регистратор,
ВЫБОР
КОГДА Продажи.Регистратор ССЫЛКА Документ.ОтгрузкаПокупателю
ТОГДА Продажи.Количество
ИНАЧЕ 0
КОНЕЦ КАК КоличествоПродажи,
ВЫБОР
КОГДА Продажи.Регистратор ССЫЛКА Документ.ВозвратОтПокупателя
ТОГДА Продажи.Количество
ИНАЧЕ 0
КОНЕЦ КАК КоличествоВозврата
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
Поле Период основной таблицы регистра проиндексировано СУБД, что гарантирует быстрое выполнение выборки без разыменования поля Регистратор.Дата и без повышенного расхода памяти.
Частая ошибка разработчиков — попытка рассчитать процент возврата на вкладке «Вычисляемые поля» по формуле:
100 * (КоличествоВозврата / КоличествоПродажи)
На уровне детальных записей каждая строка регистра сформирована конкретным документом: либо отгрузкой, либо возвратом. В строке отгрузки поле КоличествоВозврата равно нулю, а в строке возврата нулю равно КоличествоПродажи. В результате построчное деление приводит к делению на ноль и появлению пустых или ошибочных значений.
Правильный расчет процентов и коэффициентов в СКД настраивается исключительно на вкладке «Ресурсы» с предварительным агрегированием слагаемых через функцию Сумма():
ВЫБОР
КОГДА ЕстьNull(Сумма(КоличествоПродажи), 0) = 0
ТОГДА 0
ИНАЧЕ 100 * (Сумма(КоличествоВозврата) / Сумма(КоличествоПродажи))
КОНЕЦ
При такой настройке система компоновки данных сначала просуммирует количество продаж и возвратов по всей выбранной группировке (например, по номенклатуре, контрагенту или в целом по отчету) и только затем вычислит корректный процент возврата.