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