При разработке отчетов в системе «1С:Предприятие» начинающие программисты часто сталкиваются с задачей суммирования определенных показателей и вывода общего результата в строке «Итого». На первый взгляд задача кажется простой, но попытки реализовать её «в лоб» — например, через ручной перебор документов — приводят к серьезным ошибкам проектирования, падению производительности базы данных и неработающему коду.
В этой статье мы подробно проанализируем типичные ошибки при разработке подобных отчетов, разберем работу со встроенным Конструктором запросов и рассмотрим три эффективных способа расчета итогов: от простого суммирования в цикле до профессионального использования возможностей СУБД и Системы компоновки данных (СКД).
Для начала давайте детально проанализируем код, который часто пытаются использовать новички, и выясним, почему он не работает или работает неэффективно:
Выборка = Документы.РасходнаяНакладная.Выбрать();
Пока Выборка.Следующий() Цикл
ОблЭлемент.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОблЭлемент);
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст = ("Выбрать");
Результат = Запрос.Выполнить();
Итог = Результат.Выбрать();
Пока Итог.Следующий() Цикл
ОблИтого.Параметры.Заполнить(Итог);
ТабДок.Вывести(ОблИтого)
КонецЦикла;
В этом фрагменте допущено сразу несколько критических ошибок:
Документы.РасходнаяНакладная.Выбрать() заставляет платформу считывать из базы данных абсолютно все реквизиты каждого документа (включая табличные части, комментарии, служебные свойства). Если в базе данных накопятся тысячи накладных, этот код заблокирует работу системы и вызовет критическую нехватку оперативной памяти. В 1С действует жесткое правило: любые отчеты должны строиться исключительно на основе запросов.Запрос.Текст = ("Выбрать") является синтаксически неверной. Платформа 1С выдаст ошибку на этапе выполнения, так как оператор ВЫБРАТЬ (или SELECT) требует указания конкретных полей и источников данных (таблиц).Итог не имеет смысла, поскольку сам запрос не содержит ни группировок, ни агрегатных функций, способных посчитать сумму.Теперь рассмотрим правильные варианты реализации этой задачи.
Если перед нами стоит задача быстро исправить существующий алгоритм без глобального изменения структуры кода, мы можем применить ручное накопление суммы в переменную-счетчик во время обхода выборки. Этот способ прост в понимании, но помните о его низкой производительности при больших объемах данных.
Давайте реализуем этот алгоритм шаг за шагом:
&НаСервереБезКонтекста
Процедура ЗаполнитьТабДок(ТабДок)
ТабДок.Очистить();
// Получаем макет отчета
Макет = Отчеты.ОтчетПоЗаказам.ПолучитьМакет("Отчёт");
ОблШапка = Макет.ПолучитьОбласть("Шапка");
ОблЭлемент = Макет.ПолучитьОбласть("Элемент");
ОблИтого = Макет.ПолучитьОбласть("Итого");
// Выводим шапку отчета
ОблШапка.Параметры.ДатаОтчёта = Формат(ТекущаяДата(), "ДЛФ=DD");
ТабДок.Вывести(ОблШапка);
// Инициализируем переменную для накопления суммы заявок
ВсегоЗаявок = 0;
// Выбираем документы
Выборка = Документы.РасходнаяНакладная.Выбрать();
Пока Выборка.Следующий() Цикл
// Заполняем параметры области макета данными текущего документа
ОблЭлемент.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОблЭлемент);
// Накапливаем сумму. Предположим, что количество заявок хранится в реквизите "КоличествоЗаявок"
ВсегоЗаявок = ВсегоЗаявок + Выборка.КоличествоЗаявок;
КонецЦикла;
// Заполняем параметр в области "Итого" накопленным значением
// Обратите внимание: вывод происходит строго ПОСЛЕ окончания цикла!
ОблИтого.Параметры.ИтогоЗаявок = ВсегоЗаявок;
ТабДок.Вывести(ОблИтого);
КонецПроцедуры
Преимущество метода: предельная простота реализации и отсутствие необходимости разбираться в синтаксисе запросов.
Недостаток метода: крайне низкая скорость работы при росте базы данных, избыточное потребление памяти.
Это профессиональный подход. Мы переносим всю вычислительную нагрузку на СУБД. База данных сама отфильтрует, сгруппирует и сложит нужные числа, вернув серверу 1С уже готовый сжатый результат.
Для создания запроса воспользуемся встроенным инструментом — Конструктором запроса. Чтобы запустить его, выполните следующие действия:
Запрос.Текст = "";.РасходнаяНакладная в поле «Таблицы».Водитель и КоличествоЗаявок).КоличествоЗаявок в список итоговых полей и укажите функцию СУММА.Давайте посмотрим на пример программного кода, использующего такой запрос:
&НаСервереБезКонтекста
Процедура ЗаполнитьТабДок(ТабДок)
ТабДок.Очистить();
Макет = Отчеты.ОтчетПоЗаказам.ПолучитьМакет("Отчёт");
ОблШапка = Макет.ПолучитьОбласть("Шапка");
ОблЭлемент = Макет.ПолучитьОбласть("Элемент");
ОблИтого = Макет.ПолучитьОбласть("Итого");
ОблШапка.Параметры.ДатаОтчёта = Формат(ТекущаяДата(), "ДЛФ=DD");
ТабДок.Вывести(ОблШапка);
// Создаем и настраиваем запрос
Запрос = Новый Запрос;
Запрос.Text =
"ВЫБРАТЬ
| РасходнаяНакладная.Водитель КАК Водитель,
| РасходнаяНакладная.КоличествоЗаявок КАК КоличествоЗаявок
|ИЗ
| Документ.РасходнаяНакладная КАК РасходнаяНакладная
|ИТОГИ
| СУММА(КоличествоЗаявок)
|ПО
| ОБЩИЕ";
РезультатЗапроса = Запрос.Выполнить();
// Сначала получаем общие итоги по всему запросу
ВыборкаОбщиеИтоги = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
ОбщаяСуммаЗаявок = 0;
Если ВыборкаОбщиеИтоги.Следующий() Тогда
// Извлекаем заранее посчитанную СУБД сумму
ОбщаяСуммаЗаявок = ВыборкаОбщиеИтоги.КоличествоЗаявок;
КонецЕсли;
// Теперь выбираем детальные записи для вывода строк отчета
ВыборкаДетали = РезультатЗапроса.Выбрать();
Пока ВыборкаДетали.Следующий() Цикл
ОблЭлемент.Параметры.Заполнить(ВыборкаДетали);
ТабДок.Вывести(ОблЭлемент);
КонецЦикла;
// Выводим строку Итого с готовой суммой
ОблИтого.Параметры.ИтогоЗаявок = ОбщаяСуммаЗаявок;
ТабДок.Вывести(ОблИтого);
КонецПроцедуры
Преимущество метода: высокая скорость работы, минимальное использование ресурсов, соответствие стандартам разработки фирмы «1С».
Существует еще один удобный и лаконичный способ быстрого получения итогов без необходимости описывать сложную иерархию обхода группировок запроса. Мы можем выгрузить результат работы запроса в объект типа ТаблицаЗначений и воспользоваться встроенным методом Итог().
Давайте проанализируем этот пример:
&НаСервереБезКонтекста
Процедура ЗаполнитьТабДок(ТабДок)
ТабДок.Очистить();
Макет = Отчеты.ОтчетПоЗаказам.ПолучитьМакет("Отчёт");
ОблШапка = Макет.ПолучитьОбласть("Шапка");
ОблЭлемент = Макет.ПолучитьОбласть("Элемент");
ОблИтого = Макет.ПолучитьОбласть("Итого");
ОблШапка.Параметры.ДатаОтчёта = Формат(ТекущаяДата(), "ДЛФ=DD");
ТабДок.Вывести(ОблШапка);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РасходнаяНакладная.Водитель КАК Водитель,
| РасходнаяНакладная.КоличествоЗаявок КАК КоличествоЗаявок
|ИЗ
| Документ.РасходнаяНакладная КАК РасходнаяНакладная";
// Выгружаем результат выполнения запроса в Таблицу Значений
ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
// Выводим строки отчета путем обхода таблицы значений
Для Каждого СтрокаДанных Из ТаблицаРезультата Цикл
ОблЭлемент.Параметры.Заполнить(СтрокаДанных);
ТабДок.Вывести(ОблЭлемент);
КонецЦикла;
// Получаем сумму по колонке в одно действие с помощью встроенного метода Итог()
ОблИтого.Параметры.ИтогоЗаявок = ТаблицаРезультата.Итог("КоличествоЗаявок");
ТабДок.Вывести(ОблИтого);
КонецПроцедуры
Этот способ отлично сочетает в себе быстродействие полноценного запроса и простоту написания кода, исключая сложные многоуровневые выборки.
В современной практике разработки на платформе «1С:Предприятие 8.3» ручное рисование макетов и написание кода вывода табличных документов используется только в специфических задачах (например, при формировании печатных форм документов). Для построения отчетов применяется Система компоновки данных (СКД).
СКД позволяет вообще отказаться от написания программного кода. Давайте разберем, как решить нашу задачу с помощью этого механизма:
ВЫБРАТЬ
РасходнаяНакладная.Водитель КАК Водитель,
РасходнаяНакладная.КоличествоЗаявок КАК КоличествоЗаявок
ИЗ
Документ.РасходнаяНакладная КАК РасходнаяНакладная
КоличествоЗаявок в правое окно. В колонке «Выражение» автоматически установится функция СУММА(КоличествоЗаявок). Это укажет системе на необходимость расчета итогов по этому полю.Водитель) или просто добавьте структуру «Детальные записи» для вывода плоского списка.Запустите отчет в режиме «1С:Предприятие». Система автоматически сгенерирует форму, выведет данные в красивую таблицу и самостоятельно добавит итоговую строку со всеми рассчитанными суммами внизу страницы. При этом вам не пришлось писать ни одной строчки программного кода!
Используйте эти подходы в своей практике, избегайте прямых выборок объектов в циклах и доверяйте сложные расчеты механизму запросов платформы 1С.