Суммируем строки в 1С: от метода Итог() до запроса

· 9 мин

Содержание

Чтобы быстро получить сумму числового столбца у табличной части или таблицы значений, вызовите метод Итог("ИмяКолонки") — он вернёт сумму по всем строкам одной строкой кода. Для данных из базы суммируйте прямо в запросе агрегатной функцией СУММА(), а итоги по разрезам считайте секцией ИТОГИ ... ПО.

Ниже разберём каждый способ посчитать сумму строк: когда хватает готового метода, где нужен запрос, а где придётся перебирать строки в цикле. Все примеры — на встроенном языке 1С 8.3, платформа 8.3 с интерфейсом «Такси».

Сумма столбца табличной части методом Итог()

Самый короткий путь. У коллекций строк — у табличной части документа и у таблицы значений — есть метод Итог(<ИмяРеквизита>). Он складывает значения одного числового столбца по всем строкам и возвращает число.

// Сумма по колонке "Сумма" табличной части "Товары"
ВсегоПоДокументу = Объект.Товары.Итог("Сумма");

// То же для таблицы значений
ИтогКоличество = ТаблицаТоваров.Итог("Количество");

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

Что полезно помнить про Итог():

  • 📊 суммируется весь столбец целиком — отобрать часть строк методом нельзя;
  • 🔢 колонка должна быть числовой; для строкового или ссылочного реквизита метод вернёт ошибку;
  • ⚡ по скорости это лучший вариант, когда строки уже загружены в память (объект проведён, форма открыта).

Если нужна сумма не по всем строкам, а по отфильтрованным, сначала отберите их методом НайтиСтроки() в отдельную таблицу значений, а потом вызовите Итог() уже у неё.

Отбор = Новый Структура("Склад", ВыбранныйСклад);
НужныеСтроки = ТаблицаТоваров.НайтиСтроки(Отбор); // массив строк
// для суммы массива строк проще перебрать его циклом (см. ниже)

Суммирование в запросе: агрегат СУММА()

Когда данные лежат в базе и грузить их в память целиком не нужно, сумму считает сам сервер СУБД. В языке запросов за это отвечает агрегатная функция СУММА().

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

Такой запрос вернёт одну строку с итогами сразу по двум столбцам. Это основной способ для больших объёмов: по индексированному отбору сумма считается на стороне базы, а в 1С приходит готовый результат.

Несколько правил по агрегату СУММА():

  • любое поле вне агрегата должно попасть в секцию СГРУППИРОВАТЬ ПО, иначе запрос не выполнится;
  • NULL в сумме игнорируется — но результат самой СУММА() по пустой выборке равен NULL, а не нулю. Оберните его ЕСТЬNULL(СУММА(...), 0), если дальше делите или сравниваете;
  • суммировать можно и выражение: СУММА(Товары.Цена * Товары.Количество).

СУММА() по пустому результату возвращает NULL. Арифметика с NULL даёт NULL, и ошибка всплывёт уже при выводе. Привычка писать ЕСТЬNULL вокруг агрегата снимает этот класс проблем.

Итоги по группировкам: секция ИТОГИ ПО

Если нужна не одна общая сумма, а подытоги по номенклатуре, складу или месяцу, добавьте в запрос секцию ИТОГИ. Она считает промежуточные суммы прямо в результате запроса, не ломая построчную выборку.

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

В результате получится многоуровневая структура: сначала итог по складу, внутри — итоги по номенклатуре, а на самом верху — общий итог (ПО ОБЩИЕ, если добавить это слово). Порядок полей после ПО задаёт вложенность группировок.

💡 Разделяйте роли двух конструкций. СГРУППИРОВАТЬ ПО схлопывает строки в одну на каждую группу — детализация пропадает. ИТОГИ ПО оставляет все исходные строки и добавляет к ним строки-итоги. Для печатной формы с подытогами нужна именно вторая.

Как забрать итоги в коде

Результат с итогами читают обходом по группировкам:

Выборка = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока Выборка.Следующий() Цикл
    // Выборка.Сумма здесь — итог по текущему складу
    ВыборкаНоменклатуры = Выборка.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    Пока ВыборкаНоменклатуры.Следующий() Цикл
        // итог по номенклатуре внутри склада
    КонецЦикла;
КонецЦикла;

Параметр ОбходРезультатаЗапроса.ПоГруппировкам обязателен — без него выборка пойдёт по детальным записям и итоговых строк вы не увидите.

Итоги в отчёте на СКД

Когда сумма нужна не в коде, а в готовом отчёте, итоги считает система компоновки данных. В конструкторе схемы компоновки откройте вкладку «Ресурсы» и добавьте поле с выражением Сумма(Сумма) (или Сумма(Цена * Количество)). Ресурс — это и есть суммируемое поле набора данных.

Дальше СКД сама посчитает подытоги по каждой группировке и общий итог, без единой строки кода обхода. Это штатный способ для пользовательских отчётов: добавляете поле в ресурсы — получаете суммы на всех уровнях группировок автоматически.

💡 Если поле не попало в «Ресурсы», в группировках по нему будут пустые ячейки вместо сумм — частая причина жалобы «отчёт не считает итоги». Проверьте вкладку ресурсов в первую очередь.

Ручной перебор строк в цикле

Цикл — универсальный, но самый медленный способ. Он оправдан, когда сумма считается по сложному условию, которое неудобно выразить ни методом, ни запросом: например, суммируем только строки с определённым признаком и попутно что-то проверяем.

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

Несколько моментов, на которых спотыкаются:

  1. инициализируйте накопитель нулём (Сумма = 0), иначе первое сложение уйдёт в Неопределено;
  2. не склеивайте число со строкой при выводе — приводите формат явно: Формат(Сумма, "ЧДЦ=2; ЧРД=,");
  3. если внутри цикла строки ещё и удаляются, перебирайте коллекцию с конца по индексу, а не Для Каждого — иначе часть строк пропустится.
// безопасный обратный цикл, если строки удаляются
Для Сч = Объект.Товары.Количество() - 1 По 0 Цикл
    Строка = Объект.Товары[Сч];
    // ... обработка, при необходимости Объект.Товары.Удалить(Строка)
КонецЦикла;

Перебор в цикле по десяткам тысяч строк — заметная нагрузка. Если данные приходят из базы, посчитайте сумму запросом; цикл оставьте для логики, которую в запрос не вынести.

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

У таблицы значений, кроме Итог(), есть метод Свернуть(). Он сворачивает строки по группировочным колонкам и складывает суммируемые — аналог СГРУППИРОВАТЬ ПО, но в памяти.

// Схлопнуть по складу и номенклатуре, просуммировав количество и сумму
ТаблицаТоваров.Свернуть("Склад, Номенклатура", "Количество, Сумма");

После Свернуть() в таблице останутся только перечисленные колонки: по одной строке на каждую уникальную комбинацию группировок с просуммированными значениями. Это удобно, когда таблица уже собрана в коде (например, из нескольких источников) и городить запрос ради агрегации не хочется.

Для простой общей суммы по-прежнему хватает Итог() — Свернуть() нужен именно для подытогов по разрезам.

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

Отдельная история — печатные формы и отчёты, которые строятся в табличном документе (ТабличныйДокумент). Здесь «сумма строк» означает сумму значений ячеек.

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

В коде сумму ячеек в готовом макете напрямую метода Итог() нет — числа в ячейки кладутся при заполнении макета, поэтому сумму считают на этапе формирования: накапливают её в переменной и выводят в итоговую строку макета.

ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
ОбластьИтог   = Макет.ПолучитьОбласть("Итог");
Итого = 0;
Для Каждого Строка Из ДанныеОтчета Цикл
    ОбластьСтрока.Параметры.Заполнить(Строка);
    ТабДок.Вывести(ОбластьСтрока);
    Итого = Итого + Строка.Сумма;
КонецЦикла;
ОбластьИтог.Параметры.Итого = Итого;
ТабДок.Вывести(ОбластьИтог);

Так итог считается ровно по тем строкам, что попали в отчёт, и остаётся согласованным с детализацией.

💡 Проектируйте печатную форму так, чтобы итог считался из данных (методом Итог() или запросом), а в табличный документ попадало уже готовое число. Пытаться потом «вычитать» сумму из заполненных ячеек — лишняя и хрупкая работа.

Производительность на больших объёмах

Способ подбирают под объём данных и под то, где они лежат.

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

Практические ориентиры:

  • 🚀 не загружайте тысячи строк в память только ради суммы — для этого есть СУММА() в запросе;
  • 🔎 отбор в ГДЕ стройте по индексированным полям (измерения регистров, реквизиты с отбором), иначе сервер переберёт всю таблицу;
  • 🧮 если по одним и тем же данным нужны и сумма, и количество, и среднее — соберите их одним запросом, а не тремя обходами в цикле.

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

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

Как быстрее посчитать сумму строк в вашем случае?

Частые ошибки при суммировании

  • ❌ Делят на сумму из запроса без проверки на NULL. СУММА() по пустой выборке — это NULL, а не ноль. Всегда ЕСТЬNULL(СУММА(...), 0).
  • ❌ Путают СГРУППИРОВАТЬ ПО и ИТОГИ ПО. Первая убирает детальные строки, вторая их сохраняет. Для отчёта с подытогами берут ИТОГИ.
  • ❌ Складывают сумму со строкой. "Итого: " + Сумма на некоторых значениях даст не то, что ждёшь. Приводите число к строке через Формат() или СтрШаблон().
  • ❌ Удаляют строки в прямом цикле Для Каждого. Коллекция сдвигается, часть строк пропускается. Перебирайте по индексу с конца.
  • ❌ Грузят всю табличную часть ради одного числа. Если данные в базе и нужен только итог — считайте запросом, а не циклом по выгрузке.

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

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