Суммируем строки в 1С: от метода Итог() до запроса
Содержание
Чтобы быстро получить сумму числового столбца у табличной части или таблицы значений, вызовите метод Итог("ИмяКолонки") — он вернёт сумму по всем строкам одной строкой кода. Для данных из базы суммируйте прямо в запросе агрегатной функцией СУММА(), а итоги по разрезам считайте секцией ИТОГИ ... ПО.
Ниже разберём каждый способ посчитать сумму строк: когда хватает готового метода, где нужен запрос, а где придётся перебирать строки в цикле. Все примеры — на встроенном языке 1С 8.3, платформа 8.3 с интерфейсом «Такси».
Сумма столбца табличной части методом Итог()
Самый короткий путь. У коллекций строк — у табличной части документа и у таблицы значений — есть метод Итог(<ИмяРеквизита>). Он складывает значения одного числового столбца по всем строкам и возвращает число.
// Сумма по колонке "Сумма" табличной части "Товары"
ВсегоПоДокументу = Объект.Товары.Итог("Сумма");
// То же для таблицы значений
ИтогКоличество = ТаблицаТоваров.Итог("Количество");
💡 Имя колонки передаётся строкой и должно точно совпадать с именем реквизита табличной части. Регистр не важен, но лишние пробелы или опечатка дадут ошибку «Поле не найдено».
Что полезно помнить про Итог():
- 📊 суммируется весь столбец целиком — отобрать часть строк методом нельзя;
- 🔢 колонка должна быть числовой; для строкового или ссылочного реквизита метод вернёт ошибку;
- ⚡ по скорости это лучший вариант, когда строки уже загружены в память (объект проведён, форма открыта).
Если нужна сумма не по всем строкам, а по отфильтрованным, сначала отберите их методом НайтиСтроки() в отдельную таблицу значений, а потом вызовите Итог() уже у неё.
Отбор = Новый Структура("Склад", ВыбранныйСклад);
НужныеСтроки = ТаблицаТоваров.НайтиСтроки(Отбор); // массив строк
// для суммы массива строк проще перебрать его циклом (см. ниже)
Суммирование в запросе: агрегат СУММА()
Когда данные лежат в базе и грузить их в память целиком не нужно, сумму считает сам сервер СУБД. В языке запросов за это отвечает агрегатная функция СУММА().
ВЫБРАТЬ
СУММА(Товары.Сумма) КАК СуммаВсего,
СУММА(Товары.Количество) КАК КоличествоВсего
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Ссылка.Проведен
Такой запрос вернёт одну строку с итогами сразу по двум столбцам. Это основной способ для больших объёмов: по индексированному отбору сумма считается на стороне базы, а в 1С приходит готовый результат.
Несколько правил по агрегату СУММА():
- любое поле вне агрегата должно попасть в секцию
СГРУППИРОВАТЬ ПО, иначе запрос не выполнится; NULLв сумме игнорируется — но результат самойСУММА()по пустой выборке равенNULL, а не нулю. Оберните егоЕСТЬNULL(СУММА(...), 0), если дальше делите или сравниваете;- суммировать можно и выражение:
СУММА(Товары.Цена * Товары.Количество).
СУММА() по пустому результату возвращает NULL. Арифметика с NULL даёт NULL, и ошибка всплывёт уже при выводе. Привычка писать ЕСТЬNULL вокруг агрегата снимает этот класс проблем.
Итоги по группировкам: секция ИТОГИ ПО
Если нужна не одна общая сумма, а подытоги по номенклатуре, складу или месяцу, добавьте в запрос секцию ИТОГИ. Она считает промежуточные суммы прямо в результате запроса, не ломая построчную выборку.
ВЫБРАТЬ
Товары.Номенклатура КАК Номенклатура,
Товары.Склад КАК Склад,
Товары.Сумма КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ИТОГИ
СУММА(Сумма)
ПО
Склад,
Номенклатура
В результате получится многоуровневая структура: сначала итог по складу, внутри — итоги по номенклатуре, а на самом верху — общий итог (ПО ОБЩИЕ, если добавить это слово). Порядок полей после ПО задаёт вложенность группировок.
💡 Разделяйте роли двух конструкций. СГРУППИРОВАТЬ ПО схлопывает строки в одну на каждую группу — детализация пропадает. ИТОГИ ПО оставляет все исходные строки и добавляет к ним строки-итоги. Для печатной формы с подытогами нужна именно вторая.
Как забрать итоги в коде
Результат с итогами читают обходом по группировкам:
Выборка = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока Выборка.Следующий() Цикл
// Выборка.Сумма здесь — итог по текущему складу
ВыборкаНоменклатуры = Выборка.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНоменклатуры.Следующий() Цикл
// итог по номенклатуре внутри склада
КонецЦикла;
КонецЦикла;
Параметр ОбходРезультатаЗапроса.ПоГруппировкам обязателен — без него выборка пойдёт по детальным записям и итоговых строк вы не увидите.
Итоги в отчёте на СКД
Когда сумма нужна не в коде, а в готовом отчёте, итоги считает система компоновки данных. В конструкторе схемы компоновки откройте вкладку «Ресурсы» и добавьте поле с выражением Сумма(Сумма) (или Сумма(Цена * Количество)). Ресурс — это и есть суммируемое поле набора данных.
Дальше СКД сама посчитает подытоги по каждой группировке и общий итог, без единой строки кода обхода. Это штатный способ для пользовательских отчётов: добавляете поле в ресурсы — получаете суммы на всех уровнях группировок автоматически.
💡 Если поле не попало в «Ресурсы», в группировках по нему будут пустые ячейки вместо сумм — частая причина жалобы «отчёт не считает итоги». Проверьте вкладку ресурсов в первую очередь.
Ручной перебор строк в цикле
Цикл — универсальный, но самый медленный способ. Он оправдан, когда сумма считается по сложному условию, которое неудобно выразить ни методом, ни запросом: например, суммируем только строки с определённым признаком и попутно что-то проверяем.
Сумма = 0;
Для Каждого Строка Из Объект.Товары Цикл
Если Строка.Количество > 0 Тогда
Сумма = Сумма + Строка.Цена * Строка.Количество;
КонецЕсли;
КонецЦикла;
Несколько моментов, на которых спотыкаются:
- инициализируйте накопитель нулём (
Сумма = 0), иначе первое сложение уйдёт вНеопределено; - не склеивайте число со строкой при выводе — приводите формат явно:
Формат(Сумма, "ЧДЦ=2; ЧРД=,"); - если внутри цикла строки ещё и удаляются, перебирайте коллекцию с конца по индексу, а не
Для Каждого— иначе часть строк пропустится.
// безопасный обратный цикл, если строки удаляются
Для Сч = Объект.Товары.Количество() - 1 По 0 Цикл
Строка = Объект.Товары[Сч];
// ... обработка, при необходимости Объект.Товары.Удалить(Строка)
КонецЦикла;
Перебор в цикле по десяткам тысяч строк — заметная нагрузка. Если данные приходят из базы, посчитайте сумму запросом; цикл оставьте для логики, которую в запрос не вынести.
Сумма в таблице значений без цикла
У таблицы значений, кроме Итог(), есть метод Свернуть(). Он сворачивает строки по группировочным колонкам и складывает суммируемые — аналог СГРУППИРОВАТЬ ПО, но в памяти.
// Схлопнуть по складу и номенклатуре, просуммировав количество и сумму
ТаблицаТоваров.Свернуть("Склад, Номенклатура", "Количество, Сумма");
После Свернуть() в таблице останутся только перечисленные колонки: по одной строке на каждую уникальную комбинацию группировок с просуммированными значениями. Это удобно, когда таблица уже собрана в коде (например, из нескольких источников) и городить запрос ради агрегации не хочется.
Для простой общей суммы по-прежнему хватает Итог() — Свернуть() нужен именно для подытогов по разрезам.
Сумма ячеек в табличном документе
Отдельная история — печатные формы и отчёты, которые строятся в табличном документе (ТабличныйДокумент). Здесь «сумма строк» означает сумму значений ячеек.
Для пользователя всё просто: выделите мышью диапазон ячеек с числами, и внизу окна, в строке состояния, платформа покажет их сумму, количество и среднее — как в табличном редакторе. Считать вручную ничего не нужно.
В коде сумму ячеек в готовом макете напрямую метода Итог() нет — числа в ячейки кладутся при заполнении макета, поэтому сумму считают на этапе формирования: накапливают её в переменной и выводят в итоговую строку макета.
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ОбластьИтог = Макет.ПолучитьОбласть("Итог");
Итого = 0;
Для Каждого Строка Из ДанныеОтчета Цикл
ОбластьСтрока.Параметры.Заполнить(Строка);
ТабДок.Вывести(ОбластьСтрока);
Итого = Итого + Строка.Сумма;
КонецЦикла;
ОбластьИтог.Параметры.Итого = Итого;
ТабДок.Вывести(ОбластьИтог);
Так итог считается ровно по тем строкам, что попали в отчёт, и остаётся согласованным с детализацией.
💡 Проектируйте печатную форму так, чтобы итог считался из данных (методом Итог() или запросом), а в табличный документ попадало уже готовое число. Пытаться потом «вычитать» сумму из заполненных ячеек — лишняя и хрупкая работа.
Производительность на больших объёмах
Способ подбирают под объём данных и под то, где они лежат.
| Способ | Когда применять | Риск на больших данных |
|---|---|---|
Итог() | строки уже в памяти (объект, форма) | нужно сперва всё загрузить |
СУММА() в запросе | данные в базе, нужен только итог | минимальный — считает СУБД |
ИТОГИ ПО | нужны подытоги по разрезам | растёт с числом группировок |
| Цикл | сложное условие суммирования | самый медленный, память |
Свернуть() | таблица уже собрана в коде | держит всю таблицу в памяти |
Практические ориентиры:
- 🚀 не загружайте тысячи строк в память только ради суммы — для этого есть
СУММА()в запросе; - 🔎 отбор в
ГДЕстройте по индексированным полям (измерения регистров, реквизиты с отбором), иначе сервер переберёт всю таблицу; - 🧮 если по одним и тем же данным нужны и сумма, и количество, и среднее — соберите их одним запросом, а не тремя обходами в цикле.
Для регистров накопления сумму остатков и оборотов берут из виртуальных таблиц (Остатки, Обороты) — они уже агрегированы, и СУММА() по ним работает быстрее, чем по таблице движений.
И ещё одна частая потеря скорости: Итог() сам по себе быстрый, но если вызывать его внутри цикла на каждой итерации, платформа пересчитывает столбец заново каждый раз. Посчитайте сумму один раз до цикла и сохраните в переменную — внутри цикла обращайтесь уже к ней.
Объект.ТабЧасть.Итог("Сумма") — одна строка кода.СУММА(...) с ЕСТЬNULL(..., 0), без загрузки строк в память.ИТОГИ СУММА(...) ПО ..., затем обход ПоГруппировкам.Свернуть("группировки", "суммируемые").Частые ошибки при суммировании
- ❌ Делят на сумму из запроса без проверки на NULL.
СУММА()по пустой выборке — этоNULL, а не ноль. ВсегдаЕСТЬNULL(СУММА(...), 0). - ❌ Путают СГРУППИРОВАТЬ ПО и ИТОГИ ПО. Первая убирает детальные строки, вторая их сохраняет. Для отчёта с подытогами берут
ИТОГИ. - ❌ Складывают сумму со строкой.
"Итого: " + Суммана некоторых значениях даст не то, что ждёшь. Приводите число к строке черезФормат()илиСтрШаблон(). - ❌ Удаляют строки в прямом цикле
Для Каждого. Коллекция сдвигается, часть строк пропускается. Перебирайте по индексу с конца. - ❌ Грузят всю табличную часть ради одного числа. Если данные в базе и нужен только итог — считайте запросом, а не циклом по выгрузке.
Частые вопросы
Чем Итог() отличается от СУММА() в запросе?
Итог() — метод коллекции в памяти (табличная часть, таблица значений): строки уже должны быть загружены. СУММА() — агрегат языка запросов: сумму считает сервер базы, строки в память не поднимаются. На больших данных выигрывает запрос.Как посчитать сумму только по отобранным строкам табличной части?
НайтиСтроки(Отбор) и просуммируйте результат циклом, либо отфильтруйте данные условием ГДЕ в запросе и примените СУММА(). Сам метод Итог() фильтровать не умеет — он складывает весь столбец.Почему сумма из запроса получилась пустой, хотя строки есть?
ГДЕ отсёк все строки, и СУММА() вернула NULL. Проверьте условия отбора и оберните агрегат в ЕСТЬNULL(СУММА(...), 0), чтобы на выходе всегда было число.Как получить подытоги по номенклатуре и общий итог сразу?
ИТОГИ СУММА(Сумма) ПО ОБЩИЕ, Номенклатура. Слово ОБЩИЕ добавляет верхний итог по всей выборке, остальные поля задают промежуточные группировки.Можно ли одним методом свернуть таблицу значений с суммированием?
Свернуть("Склад, Номенклатура", "Сумма, Количество"). Первый параметр — колонки группировки, второй — суммируемые. В таблице останутся только эти колонки с просуммированными значениями.