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