Разработка и Конфигурация · 10 мин
Сумма и ГДЕ в запросах 1С: считаем итоги с условием
Как использовать СУММА вместе с ГДЕ в языке запросов 1С: синтаксис, примеры кода, типичные ошибки с группировкой, NULL и проверка результата.
Запрос «сумма в где 1С» обычно означает одну из двух задач: посчитать итог по полю с помощью агрегатной функции СУММА или отфильтровать данные условием ГДЕ перед суммированием. В языке запросов 1С эти конструкции работают вместе, но у них есть важное ограничение: ГДЕ применяется к строкам до группировки, а условие на уже посчитанную сумму задаётся через ИМЕЮЩИЕ.
Ниже разобран синтаксис СУММА и ГДЕ, порядок выполнения секций запроса, примеры для типовых задач (обороты по контрагенту, остатки по складу, отбор документов по сумме) и ошибки, из-за которых итог получается неверным или запрос падает с ошибкой.
Как устроены СУММА и ГДЕ в языке запросов 1С
Язык запросов 1С построен по образцу SQL, но со своими особенностями. Секция ГДЕ задаёт условие отбора строк из виртуальной таблицы или регистра: сначала отбираются записи, потом они группируются, и только затем вычисляется СУММА. Поэтому в ГДЕ нельзя ссылаться на агрегатную функцию — на момент отбора суммы ещё не существует.
Для условия по результату агрегации предусмотрена секция ИМЕЮЩИЕ (аналог HAVING). Типовая ошибка новичка — написать ГДЕ СУММА(Оборот) > 100000 и получить сообщение об ошибке «Агрегатная функция не может использоваться в данном выражении».
Базовый пример: сумма с условием отбора
Классическая задача — посчитать сумму продаж по конкретному контрагенту за период. Условие по контрагенту и датам задаётся в ГДЕ, суммирование — через СУММА с группировкой или без неё.
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
СУММА(Продажи.СуммаОборот) КАК СуммаПродаж
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи
ГДЕ
Продажи.Контрагент = &Контрагент
СГРУППИРОВАТЬ ПО
Продажи.КонтрагентОбратите внимание: параметры виртуальной таблицы (период, отбор) лучше передавать в скобках самой таблицы, а не в секции ГДЕ. Это быстрее, потому что отбор выполняется на уровне виртуальной таблицы до соединения с остальными данными. Условие в ГДЕ оставляйте для полей, которые нельзя вынести в параметры.
Если группировка не нужна (требуется одна итоговая сумма), секцию СГРУППИРОВАТЬ ПО можно опустить — запрос вернёт одну строку с итогом по всем отобранным записям.
Фильтрация по сумме: секция ИМЕЮЩИЕ
Когда нужно оставить только те группы, где сумма превышает порог, — например, контрагенты с оборотом больше заданного значения, — условие пишется в ИМЕЮЩИЕ после группировки.
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
СУММА(Продажи.СуммаОборот) КАК Оборот
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Контрагент
ИМЕЮЩИЕ
СУММА(Продажи.СуммаОборот) > &МинимальныйОборотВ ИМЕЮЩИЕ допустимо комбинировать условия: по сумме, по количеству записей (КОЛИЧЕСТВО), по максимуму. Логические операторы И, ИЛИ, НЕ работают так же, как в ГДЕ. Порядок секций фиксирован: ВЫБРАТЬ → ИЗ → ГДЕ → СГРУППИРОВАТЬ ПО → ИМЕЮЩИЕ → УПОРЯДОЧИТЬ ПО.
Сумма по условию внутри одного поля: ВЫБОР КОГДА
Частая задача — в одной строке получить несколько сумм по разным условиям: например, отдельно приход и расход, отдельно оплаченные и неоплаченные документы. Здесь ГДЕ не подходит, потому что он отсекает строки целиком. Вместо этого условие переносится внутрь агрегатной функции через конструкцию ВЫБОР КОГДА.
ВЫБРАТЬ
Расчёты.Контрагент КАК Контрагент,
СУММА(ВЫБОР
КОГДА Расчёты.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Расчёты.Сумма
ИНАЧЕ 0
КОНЕЦ) КАК Приход,
СУММА(ВЫБОР
КОГДА Расчёты.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ТОГДА Расчёты.Сумма
ИНАЧЕ 0
КОНЕЦ) КАК Расход
ИЗ
РегистрНакопления.РасчётыСКонтрагентами КАК Расчёты
СГРУППИРОВАТЬ ПО
Расчёты.КонтрагентТакой приём называют условным агрегированием. Он выполняется за один проход по таблице и обычно быстрее, чем два отдельных запроса с разными условиями в ГДЕ и последующее соединение результатов.
Типичные ошибки при работе с СУММА и ГДЕ
Первая группа ошибок — синтаксические: попытка использовать СУММА в секции ГДЕ, пропущенная группировка по неагрегированному полю, неверный порядок секций. Платформа выдаёт сообщение об ошибке при построении запроса, и такие проблемы обнаруживаются сразу.
Вторая группа опаснее — логические ошибки, при которых запрос выполняется, но сумма неверна. Самая частая причина — соединение таблиц до суммирования: если к строке документа присоединяется несколько строк другой таблицы (например, товары и оплаты), сумма задваивается или застраивается. Правильное решение — сначала сгруппировать каждую таблицу отдельно во вложенных запросах или временных таблицах, а потом соединять уже агрегированные данные по ключу.
Производительность: где ставить условия
На небольших базах разница незаметна, но на регистрах с миллионами записей расположение условия критично. Правило простое: чем раньше отсекаются лишние строки, тем быстрее запрос. Приоритет такой: параметры виртуальной таблицы (период, отбор по измерениям) → секция ГДЕ → соединения → группировка.
Для регистров накопления и сведений всегда используйте виртуальные таблицы Обороты, Остатки, ОстаткиИОбороты вместо суммирования физической таблицы движений — они хранят предрасчитанные итоги. Суммировать физическую таблицу регистра напрямую оправдано только когда нужны нестандартные разрезы, которых нет в виртуальных таблицах.
Где задавать условие
Самый быстрый вариант для периода и измерений регистра. Ограничение: доступен только для полей, предусмотренных структурой виртуальной таблицы.
Универсальный отбор по любым полям до группировки. Подходит для условий, которые нельзя вынести в параметры.
Единственный способ отфильтровать по значению СУММА, КОЛИЧЕСТВО и других агрегатов. Применяется после группировки, поэтому не сокращает объём обрабатываемых строк.
Для параллельного подсчёта нескольких сумм с разными условиями в одной строке результата.
Как проверить, что сумма посчитана правильно
После написания запроса сверьте результат с эталоном. Для оборотов по контрагенту эталоном служит типовой отчёт (например, карточка счёта или оборотно-сальдовая ведомость в бухгалтерских конфигурациях, отчёт по продажам в управленческих). Если суммы расходятся — ищите причину в отборах: границы периода (включена ли конечная дата целиком), валюта, организация, признак активности записей.
Полезный приём — выполнить тот же запрос без группировки и СУММА, вывести детальные строки и просуммировать их вручную по одной группе. Так видно, какие именно записи попали в выборку и не задвоились ли они из-за соединения.
Как проверить результат
Совпадает с типовым отчётом или ручным сложением детальных строк
Кратно больше ожидаемой — вероятно задвоение из-за соединения до группировки
Группы без движений либо отсутствуют, либо дают 0
В поле суммы NULL — мешает дальнейшим вычислениям
Документы на конечную дату включены (используйте конец дня или границу)
Документы последнего дня теряются — период задан без учёта времени
Запрос по виртуальной таблице выполняется быстро даже на большом регистре
Запрос суммирует физическую таблицу движений и заметно тормозит
Выполнение запроса из кода и получение суммы
В модуле запрос создаётся объектом Запрос, параметры устанавливаются методом УстановитьПараметр, результат получают через Выполнить().Выбрать() или Выгрузить(). Если запрос возвращает одну строку с итогом, удобно сразу обработать ситуацию пустого результата — когда по условию ГДЕ не нашлось ни одной записи, выборка будет пустой, и обращение к полю без проверки вызовет ошибку.
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ СУММА(Продажи.СуммаОборот) КАК Сумма
|ИЗ РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи
|ГДЕ Продажи.Контрагент = &Контрагент";
Запрос.УстановитьПараметр("Начало", НачалоМесяца(ТекущаяДатаСеанса()));
Запрос.УстановитьПараметр("Конец", КонецМесяца(ТекущаяДатаСеанса()));
Запрос.УстановитьПараметр("Контрагент", Контрагент);
Выборка = Запрос.Выполнить().Выбрать();
СуммаПродаж = 0;
Если Выборка.Следующий() Тогда
СуммаПродаж = ?(Выборка.Сумма = NULL, 0, Выборка.Сумма);
КонецЕсли;FAQ
Можно ли использовать СУММА в секции ГДЕ?
Нет. Секция ГДЕ выполняется до группировки, когда агрегатные значения ещё не вычислены. Условие по сумме пишите в секции ИМЕЮЩИЕ после СГРУППИРОВАТЬ ПО.
Почему СУММА вернула NULL вместо числа?
Агрегат по пустому набору строк или по строкам, где поле равно NULL, даёт NULL. Обрабатывайте это в коде через проверку на NULL или используйте ЕСТЬNULL(СУММА(Поле), 0) в тексте запроса.
Как посчитать сумму только по проведённым документам?
Если данные берутся из регистра, непроведённые документы туда не пишут движения — дополнительное условие не нужно. Если суммируете реквизиты самих документов, добавьте в ГДЕ условие Документ.Проведен = ИСТИНА.
Почему сумма в запросе больше, чем в типовом отчёте?
Чаще всего виновато соединение таблиц до группировки: каждая строка первой таблицы умножается на число совпавших строк второй. Сгруппируйте каждую таблицу отдельно во временные таблицы и соединяйте уже итоги. Также проверьте границы периода и отбор по организации.
Как получить сумму без группировки по измерениям?
Просто не указывайте СГРУППИРОВАТЬ ПО и не выбирайте неагрегированные поля. Запрос вида ВЫБРАТЬ СУММА(Поле) ИЗ ... ГДЕ ... вернёт одну строку с общим итогом по отобранным записям.
Что быстрее: СУММА по физической таблице или виртуальная таблица Обороты?
Виртуальная таблица Обороты почти всегда быстрее, потому что использует предрасчитанные итоги регистра. Прямое суммирование физической таблицы оправдано только для разрезов, недоступных в виртуальных таблицах.
Что важно запомнить
ГДЕ отбирает строки до суммирования, ИМЕЮЩИЕ фильтрует по готовой сумме, ВЫБОР КОГДА внутри СУММА даёт несколько условных итогов в одной строке. Отборы по периоду и измерениям выносите в параметры виртуальной таблицы, соединяйте таблицы после группировки, а результат всегда сверяйте с типовым отчётом.