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