Сумма по колонке в 1С: четыре способа для разработчика и пользователя

· 9 мин

Содержание

Нужна сумма по колонке — берите ту точку, где данные уже лежат. Данные в базе: агрегатная функция СУММА() в запросе. Готовый отчёт: ресурс в СКД. Данные уже в памяти: Итог() у таблицы значений. Ячейки в табличном документе: выделите их и смотрите поле суммы в строке состояния. Цикл по строкам — последний вариант, не первый.

Типичная история: разработчик выгрузил запросом 50 000 строк реализаций в таблицу значений и считает сумму циклом на клиенте. Работает, но медленно и без нужды. Ниже разбираем все четыре точки входа с рабочим кодом и показываем, где какая быстрее.

Сумма в запросе: СУММА, ИТОГИ и группировки

Если нужна одна цифра или цифры по группам, считайте на стороне СУБД. Сервер вернёт уже готовую сумму, а не тысячи строк.

Одно число на всю выборку

Запрос = Новый Запрос;
Запрос.Текст =
	"ВЫБРАТЬ
	|	ЕСТЬNULL(СУММА(Т.Сумма), 0) КАК Итого
	|ИЗ
	|	Документ.РеализацияТоваровУслуг.Товары КАК Т
	|ГДЕ
	|	Т.Ссылка.Проведен
	|	И Т.Ссылка.Дата МЕЖДУ &Начало И &Конец";
Запрос.УстановитьПараметр("Начало", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("Конец", КонецМесяца(ТекущаяДата()));
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
	Итого = Выборка.Итого;
КонецЕсли;

Смотрите на ЕСТЬNULL. Если под условие не попало ни одной строки, СУММА вернёт NULL, а не ноль. Дальше это ломает арифметику и сравнения.

Сумма по группам

Добавьте СГРУППИРОВАТЬ ПО и получите по строке на каждое значение поля:

ВЫБРАТЬ
	Т.Номенклатура КАК Номенклатура,
	СУММА(Т.Сумма) КАК Сумма
ИЗ
	Документ.РеализацияТоваровУслуг.Товары КАК Т
СГРУППИРОВАТЬ ПО
	Т.Номенклатура

Итоги вместе с детальными записями

Нужны и строки, и подытоги? Не гоняйте два запроса. Секция ИТОГИ отдаёт иерархию: детальные записи плюс суммы по группам и общий итог.

ВЫБРАТЬ
	Т.Ссылка.Контрагент КАК Контрагент,
	Т.Ссылка КАК Документ,
	Т.Сумма КАК Сумма
ИЗ
	Документ.РеализацияТоваровУслуг.Товары КАК Т
ИТОГИ
	СУММА(Сумма)
ПО
	ОБЩИЕ,
	Контрагент

Результат обходят с Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам). Строка ОБЩИЕ даёт один итог по всей выборке.

Если в запросе нужна сумма только по условию, используйте СУММА(ВЫБОР КОГДА Условие ТОГДА Поле ИНАЧЕ 0 КОНЕЦ). Это одна выборка вместо нескольких запросов с разными отборами.

Сумма в СКД: ресурсы и вычисляемые поля

В отчётах на системе компоновки данных сумму не пишут руками. Её задают как ресурс.

Ресурс в схеме

В конфигураторе откройте схему компоновки, перейдите на закладку «Ресурсы» и добавьте поле. По умолчанию выражение выглядит как Сумма(Сумма). Колонка тогда суммируется на каждом уровне группировки и в общем итоге.

В поле «Рассчитывать по…» перечисляют группировки, где ресурс считается. Пусто — считается везде.

Особенность: сумма не равна сумме на разных уровнях

Бывает, что на уровне группировки нужно считать иначе, чем в итоге. Например, сумму остатков по складам складывать нельзя по датам, а только по складам. Для этого в ресурсе задают отдельные выражения для разных группировок: для одной Сумма(Остаток), для другой Максимум(Остаток).

Что видит пользователь

Пользователь работает с ресурсом через вариант отчёта. В настройках отчёта есть закладка с итогами, и нужная колонка отображается в строке «Итого», если для поля разрешено суммирование.

ЗадачаВыражение ресурса
Сумма по колонкеСумма(Сумма)
Количество строкКоличество(Ссылка)
СреднееСреднее(Цена)
Сумма только положительныхСумма(ВЫБОР КОГДА Сумма > 0 ТОГДА Сумма ИНАЧЕ 0 КОНЕЦ)

Если колонка в отчёте пустая, а ресурс объявлен, проверьте, что у поля не стоит ограничение «Использование поля» в наборе данных и что оно не отключено в настройках варианта.

Сумма в коде: таблица значений и цикл

Данные уже загружены в таблицу значений — например, это результат внешнего файла или промежуточная обработка. Тогда запрос не нужен.

Метод Итог()

Самое короткое решение:

Итого = ТаблицаЗначений.Итог("Сумма");

Метод принимает имя колонки и возвращает число. Значения неопределённого типа и пустые ячейки он пропускает. Колонка строкового типа не даст осмысленного результата, так что тип проверяйте заранее.

Свернуть: суммы по группам

Аналог СГРУППИРОВАТЬ ПО для таблицы значений:

ТаблицаЗначений.Свернуть("Номенклатура", "Сумма");

Первый параметр — колонки группировки, второй — колонки, которые суммируются. Остальные колонки таблица потеряет. Если они нужны, работайте с копией: Копия = ТаблицаЗначений.Скопировать();.

Ручной цикл

Нужен, когда в сумму идут не все строки или значения приходится приводить:

Итого = 0;
Для Каждого Строка Из ТаблицаЗначений Цикл
	Если Строка.Сумма > 0 Тогда
		Итого = Итого + Строка.Сумма;
	КонецЕсли;
КонецЦикла;

Условие в цикле часто можно заменить: сначала НайтиСтроки() по структуре отбора, затем цикл по найденным. На больших таблицах это быстрее, чем перебирать всё.

Табличная часть документа

С табличной частью то же самое: Объект.Товары.Итог("Сумма") у реквизита-табличной части. Метод Итог() есть и у табличной части на форме, и у данных формы после загрузки. Поле «итого» в подвале колонки формы настраивают свойством колонки «Путь к данным подвала»: для табличной части Товары и колонки Сумма путь обычно Объект.Товары.ИтогСумма.

Нужна сумма с условием? Идите через Выгрузить(): Объект.Товары.Выгрузить(Отбор), затем Итог() у результата.

Сумма в табличном документе

Здесь две разные задачи: пользователь хочет быстро посмотреть цифру, а программист — вычислить её в обработке.

Для пользователя

Выделите нужные ячейки мышью. В правом нижнем углу окна в строке состояния появится поле с символом суммы Σ — там отображается сумма выделенных числовых ячеек. Работает и в отчётах, и в печатных формах. Если поля не видно, включите отображение панели в настройках интерфейса.

Для программиста

Табличный документ хранит текст ячеек, а не числа. Значит, сначала число нужно получить из области:

Итого = 0;
Для НомерСтроки = 5 По ТабДок.ВысотаТаблицы Цикл
	Область = ТабДок.Область(НомерСтроки, 3, НомерСтроки, 3);
	Текст = СтрЗаменить(Область.Текст, Символы.НПП, "");
	Текст = СтрЗаменить(Текст, " ", "");
	Если ПустаяСтрока(Текст) Тогда
		Продолжить;
	КонецЕсли;
	Итого = Итого + Число(СтрЗаменить(Текст, ",", "."));
КонецЦикла;

Здесь три ловушки. Разделитель групп разрядов — неразрывный пробел, поэтому СтрЗаменить убирает и обычный, и Символы.НПП. Десятичный разделитель может быть запятой. Пустые ячейки нужно отсеять до преобразования, иначе Число("") выдаст ноль, а с нечисловым текстом упадёт ошибка.

Если таблицу формирует ваш код, не суммируйте готовый результат вывода. Посчитайте сумму до вывода в запросе или в таблице значений и выведите её в строку итога. Это проще и надёжнее.

Если нужна формула прямо в ячейке, у области можно задать заполнение «Выражение» и ссылаться на другие ячейки. Это годится для небольших шаблонов вроде печатных форм, но не для отчётов с тысячами строк.

Сумма по регистрам: обороты и остатки

Суммы в учётных регистрах считают через виртуальные таблицы, а не через основную таблицу регистра. Платформа заранее хранит итоги, поэтому такой запрос читает меньше данных.

У регистра накопления есть виртуальная таблица Обороты с ресурсами вида СуммаОборот, СуммаПриход, СуммаРасход. Для остатков — таблица Остатки с полями СуммаОстаток. Колонка в виртуальной таблице уже суммирована по измерениям, которые вы задали в параметрах.

ВЫБРАТЬ
	О.Номенклатура КАК Номенклатура,
	О.СуммаОборот КАК Сумма
ИЗ
	РегистрНакопления.Продажи.Обороты(&Начало, &Конец, Месяц, ) КАК О

Параметр периодичности задаёт шаг: День, Месяц, Год или пусто. Если периодичность пустая, получите одну сумму за весь интервал. Если указан Месяц, на каждую комбинацию измерений придёт по строке за месяц, и общий итог придётся считать отдельно — через обёртку с СУММА() или секцию ИТОГИ.

Условия по измерениям лучше передавать в параметр виртуальной таблицы (последний параметр — отбор), а не в ГДЕ внешнего запроса. Тогда СУБД фильтрует до агрегации.

Доля от общего итога

Частая задача: показать, сколько процентов от всей суммы приходится на строку. В запросе с ИТОГИ ПО ОБЩИЕ общий итог доступен при обходе по группировкам. В СКД проще: добавьте вычисляемое поле с выражением Сумма / Сумма(Сумма), а в ресурсах отметьте, по какой группировке считать долю. Считайте долю от неокруглённых сумм, иначе процентов в сумме выйдет 99,9 или 100,1.

Какой способ быстрее

Разница заметна только на больших объёмах. Ориентиры по принципу, а не по замерам:

СпособГде считаетсяКогда выбиратьРиск
СУММА() в запросеСУБДДанные в базе, нужен итогNULL при пустой выборке
Ресурс СКДСУБД и компоновщикОтчёты для пользователяНеверные уровни группировки
Итог() у таблицыПамять сервера или клиентаДанные уже загруженыНечисловая колонка
ЦиклПамятьСложное условиеМедленно на больших объёмах
Обход ячеекПамятьРезультат уже выведенФорматирование чисел

Ещё один фактор — место выполнения. Тяжёлый цикл на клиенте при каждой строке может обращаться к серверу, и время вырастает в разы. Считайте на сервере одним вызовом, а клиенту отдавайте число.

Пример с цифрами

В таблице 100 000 строк, нужна сумма по колонке. Запрос вернёт одну строку: по сети уйдёт одно число. Цикл заберёт все 100 000 строк в память, а затем пройдёт по ним. Память под такую таблицу измеряется десятками мегабайт, а запрос отработает за долю этого времени. Если сумма нужна только по одному контрагенту, добавьте условие в ГДЕ — тогда запрос ещё и прочитает меньше записей.

Где у вас лежат данные?

Типичные ошибки при суммировании

Большая часть неверных сумм сводится к нескольким причинам.

  • ➖ NULL вместо нуля. СУММА по пустой выборке даёт NULL. Оберните в ЕСТЬNULL(..., 0).
  • ➖ Дубли из-за соединения. ЛЕВОЕ СОЕДИНЕНИЕ с таблицей, где несколько строк на одну запись, размножает суммы. Сначала сгруппируйте вложенным запросом или временной таблицей, потом соединяйте.
  • ➖ Деление и округление. Если суммируете проценты или доли, сложение округлённых значений даёт копеечные расхождения. Суммируйте исходные величины, округляйте итог.
  • ➖ Тип колонки. Колонка типа «Строка» не суммируется. Приведите значение через Число() до суммирования.
  • ➖ Ресурс СКД на неверной группировке. Итог показывает не то, что ожидалось, потому что выражение считается на другом уровне.
  • ➖ Непроведённые документы. Запрос по таблице документа учитывает и черновики. Добавьте условие Проведен, если оно нужно.

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

Частые вопросы

Почему СУММА в запросе вернула пусто, а не ноль?
Выборка оказалась пустой, и агрегатная функция вернула NULL. Оберните выражение в ЕСТЬNULL(СУММА(Поле), 0).
Как просуммировать колонку таблицы значений с условием?
Либо цикл с Если внутри, либо НайтиСтроки() по отбору и сумма по найденным строкам. Второй вариант быстрее на больших таблицах.
Можно ли получить итог в подвале колонки на форме?
Да. У колонки таблицы формы задают «Путь к данным подвала». Для табличной части документа обычно подходит путь вида Объект.Товары.ИтогСумма.
Чем `Свернуть()` отличается от `Итог()`?
Итог() возвращает одно число по колонке. Свернуть() меняет сам объект: группирует строки и суммирует выбранные колонки.
Как сложить числа, которые в табличном документе выведены текстом?
Уберите пробелы и Символы.НПП, замените запятую на точку и приведите результат через Число(). Пустые строки пропустите заранее.