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