Как сложить значения полей в запросе: СУММА, группировка и итоги
Содержание
Чтобы сложить значения поля в запросе 1С, оберните поле в функцию СУММА() и перечислите в секции СГРУППИРОВАТЬ ПО все поля, которые остаются в выборке без агрегата. Это и есть минимальный рабочий шаблон: СУММА(Таблица.Сумма) плюс группировка по номенклатуре, складу или организации — тому разрезу, в котором нужен итог.
Разберём суммирование по шагам: как устроена функция СУММА, чем группировка по нескольким измерениям отличается от одного, когда вместо ручной агрегации выгоднее взять готовую виртуальную таблицу регистра, как считать сумму по условию через ВЫБОР КОГДА и как получить многоуровневые итоги секцией ИТОГИ. Отдельно — про NULL, из-за которого суммы молча «теряются».
Все примеры — на русском языке
запросов платформы 8.3. Английские синонимы (SUM, GROUP BY) работают так же, но в типовых конфигурациях принят русский вариант.
Функция СУММА и другие агрегаты
СУММА() — агрегатная функция: она сворачивает множество строк в одно значение. Внутри скобок — числовое поле или выражение.
ВЫБРАТЬ
СУММА(Продажи.Сумма) КАК ВсегоПродаж
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Продажи
Без группировки такой запрос вернёт одну строку — общий итог по всей таблице. Как только к СУММА добавляется обычное (неагрегированное) поле, появляется требование группировки — об этом ниже.
Рядом с СУММА в языке запросов есть ещё четыре агрегатные функции. Они подчиняются тем же правилам:
- 🔢
КОЛИЧЕСТВО(*)— число строк в группе;КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле)— число уникальных значений. - ⬇️
МИНИМУМ(Поле)— наименьшее значение. - ⬆️
МАКСИМУМ(Поле)— наибольшее значение. - ➗
СРЕДНЕЕ(Поле)— среднее арифметическое.
Псевдоним через КАК задавайте всегда. Иначе колонка в результате получит служебное имя вроде Поле1, и обращение Выборка.ВсегоПродаж в коде обработки упадёт.
Сумму можно считать не только по полю, но и по выражению. Платформа сначала вычислит выражение в каждой строке, потом сложит:
ВЫБРАТЬ
СУММА(Товары.Количество * Товары.Цена) КАК Стоимость
ИЗ
Документ.ПоступлениеТоваровУслуг.Товары КАК Товары
Группировка по одному и нескольким измерениям
Группировка отвечает на вопрос «в каком разрезе считать итог». Секция СГРУППИРОВАТЬ ПО перечисляет поля, по которым строки объединяются в группы; для каждой группы агрегат считается отдельно.
Правило жёсткое: каждое поле выборки, которое не внутри агрегатной функции, обязано быть в СГРУППИРОВАТЬ ПО. Пропустите его — получите ошибку «Поле не входит в группу и не является агрегатной функцией».
Группировка по одному измерению — сумма продаж по каждой номенклатуре:
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
СУММА(Продажи.Сумма) КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
Группировка по нескольким измерениям — сумма в разрезе «склад + номенклатура». В выборке два неагрегированных поля, оба уходят в группировку:
ВЫБРАТЬ
Продажи.Склад КАК Склад,
Продажи.Номенклатура КАК Номенклатура,
СУММА(Продажи.Сумма) КАК Сумма,
КОЛИЧЕСТВО(*) КАК СтрокДокументов
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Склад,
Продажи.Номенклатура
УПОРЯДОЧИТЬ ПО
Склад,
Сумма УБЫВ
Чем больше полей в группировке, тем детальнее разрез и тем больше строк в результате. Крайний случай — группировка по всем полям таблицы: суммировать там нечего, каждая строка сама себе группа.
Отбор по результату агрегата (СУММА > 10000) нельзя поставить в ГДЕ — секция ГДЕ работает до группировки. Для фильтра по итогу используйте ИМЕЮЩИЕ.
...
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
ИМЕЮЩИЕ
СУММА(Продажи.Сумма) > 10000
Разница по секциям:
| Секция | Когда применяется | По чему фильтрует |
|---|---|---|
ГДЕ | до группировки | по значениям строк |
ИМЕЮЩИЕ | после группировки | по значению агрегата |
Виртуальные таблицы вместо ручной суммы по регистру
Когда сумма нужна по регистру накопления или бухгалтерии, ручная агрегация по таблице движений — почти всегда ошибка производительности. У регистров есть виртуальные таблицы, которые отдают уже свёрнутые данные.
- 📊
РегистрНакопления.<Имя>.Обороты— сумма приходов и расходов за период. - 📦
РегистрНакопления.<Имя>.Остатки— остаток на дату. - 🔄
РегистрНакопления.<Имя>.ОстаткиИОбороты— и то, и другое.
Вместо того чтобы складывать строки движений самому:
// Так делать не стоит на больших регистрах
ВЫБРАТЬ
Движения.Номенклатура,
СУММА(Движения.Количество) КАК Количество
ИЗ
РегистрНакопления.Продажи КАК Движения
СГРУППИРОВАТЬ ПО
Движения.Номенклатура
берите готовый оборот — платформа посчитает его по итоговым таблицам регистра, а не перебором всех движений:
ВЫБРАТЬ
Обороты.Номенклатура,
Обороты.КоличествоОборот КАК Количество,
Обороты.СуммаОборот КАК Сумма
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , )
КАК Обороты
В виртуальной таблице ресурсы уже просуммированы — к имени ресурса платформа добавляет суффикс (СуммаОборот, КоличествоОстаток). Группировать и вызывать СУММА вручную не нужно, разрез задаётся параметрами и полями измерений.
Параметры виртуальной таблицы — период и отбор — ставьте прямо в скобках таблицы, а не в секции ГДЕ. Так отбор применяется при формировании виртуальной таблицы, до соединений, и запрос читает меньше данных.
Если всё же суммируете таблицу движений напрямую (например, по своему регистру без нужной периодичности), убедитесь, что в отборе участвуют индексированные измерения — иначе платформа прочитает регистр целиком.
Условное суммирование через ВЫБОР КОГДА
Частая задача — сложить не всё подряд, а по условию: отдельно приход и расход, отдельно продажи по одной организации. Аналог SUM(CASE WHEN ...) в 1С — функция ВЫБОР внутри СУММА.
Внутри агрегата выражение ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ 0 КОНЕЦ возвращает либо значение, либо ноль, и дальше суммируется:
ВЫБРАТЬ
Движения.Номенклатура КАК Номенклатура,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Движения.Сумма
ИНАЧЕ 0
КОНЕЦ) КАК Приход,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ТОГДА Движения.Сумма
ИНАЧЕ 0
КОНЕЦ) КАК Расход
ИЗ
РегистрНакопления.Продажи КАК Движения
СГРУППИРОВАТЬ ПО
Движения.Номенклатура
Так в одну строку результата попадают сразу две суммы — приход и расход по каждой номенклатуре. Это удобнее, чем делать два запроса и соединять их.
Ссылку на предопределённое значение (вид движения, статус, значение перечисления) задавайте через ЗНАЧЕНИЕ(...), а не строкой — строковое сравнение имени не сработает.
Проверьте, что вы усвоили разницу между секциями:
Многоуровневые итоги секцией ИТОГИ
Группировка даёт плоскую таблицу итогов. Когда нужен отчёт с подытогами по уровням и общим итогом внизу, применяют отдельную секцию — ИТОГИ ... ПО.
ИТОГИ не заменяет группировку, а достраивает к детальным строкам дополнительные строки-итоги. Результат получается иерархическим — его удобно обходить деревом.
ВЫБРАТЬ
Продажи.Организация КАК Организация,
Продажи.Номенклатура КАК Номенклатура,
Продажи.Сумма КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Продажи
ИТОГИ
СУММА(Сумма)
ПО
ОБЩИЕ,
Организация
Что здесь происходит:
- Детальные строки остаются как есть — по каждой строке документа.
- Ключевое слово
ОБЩИЕдобавляет одну строку с итогом по всему результату. - Поле
ОрганизацияпослеПОдаёт подытог по каждой организации.
Порядок полей после ПО задаёт вложенность уровней: сначала более общий, потом более детальный.
Читать такой результат построчно бессмысленно — итоговые строки перемешаются с детальными. Обходите его по уровням группировок:
Выборка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока Выборка.Следующий() Цикл
// здесь Выборка.Сумма — итог по организации
ВыборкаДетали = Выборка.Выбрать();
Пока ВыборкаДетали.Следующий() Цикл
// детальные строки организации
КонецЦикла;
КонецЦикла;
Для отчётов на СКД ИТОГИ вручную писать не нужно — ресурсы и итоги по группировкам компоновщик считает сам. Секция ИТОГИ нужна, когда вы обрабатываете результат запроса кодом.
NULL при суммировании и типичные ошибки
NULL — не ноль и не пустая строка, а «значение отсутствует». В суммировании он ведёт себя неочевидно, и именно на нём чаще всего теряются деньги в отчёте.
Как СУММА обращается с NULL:
СУММАпропускаетNULL— такие строки в сложение не входят. Если все значения в группеNULL, результат тожеNULL, а не ноль.- В арифметике любой
NULL«заражает» выражение:100 + NULLдаётNULL, а не100. Это бьёт по суммам с умножением и по соединениям. - Чаще всего
NULLприходит изЛЕВОЕ СОЕДИНЕНИЕ: строки без пары во второй таблице отдаютNULLво всех её полях.
Лечится одной функцией — ЕСТЬNULL(Поле, 0). Она подставляет ноль вместо отсутствующего значения до суммирования:
ВЫБРАТЬ
Товары.Номенклатура КАК Номенклатура,
СУММА(ЕСТЬNULL(Остатки.КоличествоОстаток, 0)) КАК Остаток
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ПО Остатки.Номенклатура = Товары.Ссылка
СГРУППИРОВАТЬ ПО
Товары.Номенклатура
Диагностика частых сбоев при суммировании:
Ещё две частые ошибки по сути, а не по синтаксису:
- 🧮 Суммирование таблицы движений вместо виртуальной таблицы оборотов — запрос работает, но на большом регистре тормозит. Берите
.Обороты. - 📐 Суммирование до соединения перепутано с суммированием после. Если соединение размножает строки, сначала сворачивайте в подзапросе, потом соединяйте, иначе сумма вырастет кратно.
Суммы по периодам: обороты и итоги по месяцам
Отдельная задача — сложить не всё за интервал, а разложить сумму по месяцам, кварталам или дням. Делать это вручную через группировку по выражению с датой неудобно и медленно. Платформа даёт два штатных способа.
Через периодичность виртуальной таблицы. У таблицы Обороты третий параметр — периодичность. Укажите Месяц, и платформа добавит в результат поле Период, а обороты разложит по месяцам:
ВЫБРАТЬ
Обороты.Период КАК Период,
Обороты.Номенклатура КАК Номенклатура,
Обороты.СуммаОборот КАК Сумма
ИЗ
РегистрНакопления.Продажи.Обороты(
&НачалоПериода, &КонецПериода, Месяц, ) КАК Обороты
УПОРЯДОЧИТЬ ПО
Период
Допустимые значения периодичности — Авто, Год, Полугодие, Квартал, Месяц, Декада, Неделя, День и мельче. Суммировать после этого ничего не нужно: обороты уже свёрнуты по каждому периоду.
Через ИТОГИ ПЕРИОДАМИ. Когда данные берутся из документа, а не из регистра, разложить сумму по месяцам помогает ИТОГИ ... ПЕРИОДАМИ:
ВЫБРАТЬ
Продажи.Ссылка.Дата КАК ДатаДокумента,
Продажи.Сумма КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Продажи
ИТОГИ
СУММА(Сумма)
ПО
ДатаДокумента ПЕРИОДАМИ(МЕСЯЦ, &НачалоПериода, &КонецПериода)
Главное преимущество ПЕРИОДАМИ перед обычной группировкой по дате — платформа добавит строки-итоги даже для месяцев, в которых продаж не было. В отчёте не появится «дыр», и график строится ровно по всем периодам интервала.
Если нужна просто сумма уникальных значений (например, сумма по различным договорам без повторов), работает СУММА(РАЗЛИЧНЫЕ Поле) — повторяющиеся значения складываются один раз. Применяйте осознанно: чаще нужна обычная СУММА, а РАЗЛИЧНЫЕ — редкий случай.
Частые вопросы
Чем ИТОГИ отличаются от СГРУППИРОВАТЬ ПО?
СГРУППИРОВАТЬ ПО сворачивает строки в группы и возвращает плоскую таблицу. ИТОГИ ... ПО оставляет детальные строки и добавляет к ним иерархические строки-итоги — результат становится деревом, которое обходят по группировкам.Как сложить два поля в одно значение?
СУММА(Таблица.Сумма1 + Таблица.Сумма2). Если какое-то из полей может быть NULL, оберните его в ЕСТЬNULL, иначе вся сумма строки станет NULL.Почему СУММА возвращает NULL, а не ноль?
NULL), результат агрегата тоже NULL. Чтобы гарантировать число, примените ЕСТЬNULL(СУММА(Поле), 0) или оберните поле до суммирования.Можно ли отфильтровать строки по итоговой сумме?
ИМЕЮЩИЕ — эта секция работает после группировки. В ГДЕ отбор по агрегату поставить нельзя, она применяется до свёртки.Как посчитать сумму по условию, не делая два запроса?
СУММА(ВЫБОР КОГДА Условие ТОГДА Поле ИНАЧЕ 0 КОНЕЦ). В одной выборке так получают сразу несколько сумм по разным условиям — например, приход и расход в отдельных колонках.