Сумма по колонке в 1С: четыре способа для разработчика и пользователя
Содержание
Нужна сумма по колонке — берите ту точку, где данные уже лежат. Данные в базе: агрегатная функция СУММА() в запросе. Готовый отчёт: ресурс в СКД. Данные уже в памяти: Итог() у таблицы значений. Ячейки в табличном документе: выделите их и смотрите поле суммы в строке состояния. Цикл по строкам — последний вариант, не первый.
Типичная история: разработчик выгрузил запросом 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).Как просуммировать колонку таблицы значений с условием?
Если внутри, либо НайтиСтроки() по отбору и сумма по найденным строкам. Второй вариант быстрее на больших таблицах.Можно ли получить итог в подвале колонки на форме?
Объект.Товары.ИтогСумма.Чем `Свернуть()` отличается от `Итог()`?
Итог() возвращает одно число по колонке. Свернуть() меняет сам объект: группирует строки и суммирует выбранные колонки.Как сложить числа, которые в табличном документе выведены текстом?
Символы.НПП, замените запятую на точку и приведите результат через Число(). Пустые строки пропустите заранее.