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