СГРУППИРОВАТЬ ПО после ОБЪЕДИНИТЬ ВСЕ: один итог по всем частям запроса
Содержание
Чтобы сгруппировать объединенные запросы в 1С, оберните весь блок ОБЪЕДИНИТЬ ВСЕ во вложенный запрос (или сложите его во временную таблицу через ПОМЕСТИТЬ) и уже во внешнем запросе напишите СГРУППИРОВАТЬ ПО с СУММА, КОЛИЧЕСТВО, МАКСИМУМ. Если поставить СГРУППИРОВАТЬ ПО просто в конце объединения, группировка сработает только для последней части.
Это правило одинаково для всех конфигураций на платформе 8.3: БП, ЗУП, УТ, ERP, самописных баз. Оно относится к языку запросов, а не к прикладному решению. Ниже — синтаксис, рабочий код и разбор ошибок, на которых спотыкаются даже опытные разработчики.
Как устроено объединение в языке запросов 1С
Оператор объединения склеивает результаты нескольких ВЫБРАТЬ в одну таблицу «по вертикали»: строки второго запроса дописываются под строками первого.
Есть два варианта:
| Оператор | Что делает с дублями | Скорость | Когда брать |
|---|---|---|---|
ОБЪЕДИНИТЬ | Убирает полностью одинаковые строки | Медленнее: СУБД ищет дубли | Нужен список уникальных значений |
ОБЪЕДИНИТЬ ВСЕ | Оставляет все строки | Быстрее | Почти всегда, особенно перед группировкой |
Правила, которые проверяет платформа:
- 📐 Число полей во всех частях одинаковое. Не хватает поля — подставьте заглушку:
0,"",NULL,ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка). - 🔢 Поля сопоставляются по порядку, а не по имени. Псевдонимы во второй и следующих частях ни на что не влияют.
- 🏷️ Имена колонок итоговой таблицы берутся из первого запроса.
- 🧩 Тип поля результата — объединение типов из всех частей. Если в первой части ссылка на договор, а во второй — на контрагента, поле станет составного типа.
- 📌
УПОРЯДОЧИТЬ ПО,ИТОГИиАВТОУПОРЯДОЧИВАНИЕпишутся один раз, в самом конце, и действуют на весь результат. - 🧱
СГРУППИРОВАТЬ ПО,ИМЕЮЩИЕ,ГДЕ— свои у каждой части.
Последний пункт и создаёт главную путаницу.
Почему СГРУППИРОВАТЬ ПО в конце не группирует всё объединение
Посмотрите на типичную попытку получить продажи минус возвраты по номенклатуре:
ВЫБРАТЬ
Товары.Номенклатура КАК Номенклатура,
Товары.Сумма КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Возвраты.Номенклатура,
-Возвраты.Сумма
ИЗ
Документ.ВозвратТоваровОтПокупателя.Товары КАК Возвраты
СГРУППИРОВАТЬ ПО
Возвраты.Номенклатура
Здесь СГРУППИРОВАТЬ ПО принадлежит второму ВЫБРАТЬ. Платформа сразу выдаст ошибку Поле не входит в группу: поле -Возвраты.Сумма не обёрнуто в агрегатную функцию.
Допустим, вы исправили это на СУММА(-Возвраты.Сумма). Ошибка уйдёт, но результат будет неправильным:
- строки реализаций останутся построчными, по одной на каждую строку табличной части;
- возвраты свернутся по номенклатуре;
- одна и та же позиция появится в отчёте несколько раз.
Запрос, который выполнился без ошибки, ещё не значит, что он считает правильно. Проверяйте результат на номенклатуре, у которой есть и продажи, и возвраты.
Вывод простой: группировка по всему объединению делается на уровень выше. Для этого есть три способа.
Три способа свернуть результат объединения
| Способ | Когда подходит | Минусы |
|---|---|---|
Вложенный запрос в ИЗ | Разовая выборка, объём умеренный | Длинный вложенный текст хуже читается и отлаживается |
Временная таблица ПОМЕСТИТЬ | Пакетный запрос, результат нужен ещё раз, большой объём | Больше текста, нужен МенеджерВременныхТаблиц при нескольких запросах |
| Набор данных «Объединение» в СКД | Отчёт на системе компоновки | Только внутри СКД, не для кода обработки |
Способ 1. Вложенный запрос
Весь блок с ОБЪЕДИНИТЬ ВСЕ помещается в скобки после ИЗ и получает псевдоним. Внешний запрос видит его как обычную таблицу:
ВЫБРАТЬ
Движения.Номенклатура КАК Номенклатура,
СУММА(Движения.Сумма) КАК Сумма
ИЗ
(ВЫБРАТЬ
Товары.Номенклатура КАК Номенклатура,
Товары.Сумма КАК Сумма
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Ссылка.Проведен
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Возвраты.Номенклатура,
-Возвраты.Сумма
ИЗ
Документ.ВозвратТоваровОтПокупателя.Товары КАК Возвраты
ГДЕ
Возвраты.Ссылка.Проведен) КАК Движения
СГРУППИРОВАТЬ ПО
Движения.Номенклатура
Способ 2. Временная таблица
ПОМЕСТИТЬ пишется в первой части объединения и забирает в таблицу результат всех частей сразу:
ВЫБРАТЬ
Товары.Номенклатура КАК Номенклатура,
Товары.Сумма КАК Сумма
ПОМЕСТИТЬ ВТ_Движения
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Ссылка.Проведен
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Возвраты.Номенклатура,
-Возвраты.Сумма
ИЗ
Документ.ВозвратТоваровОтПокупателя.Товары КАК Возвраты
ГДЕ
Возвраты.Ссылка.Проведен
;
////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Движения.Номенклатура КАК Номенклатура,
СУММА(ВТ_Движения.Сумма) КАК Сумма
ИЗ
ВТ_Движения КАК ВТ_Движения
СГРУППИРОВАТЬ ПО
ВТ_Движения.Номенклатура
Это пакет из двух запросов, разделённых ;. Результат возвращает последний.
Способ 3. Объединение в СКД
В схеме компоновки данных добавьте набор данных типа «Объединение» и внутри него — два набора-запроса. Поля сопоставляются по имени, а не по порядку.
Сами запросы группировку не делают. Сумма отмечается как ресурс на закладке «Ресурсы», и компоновка свернёт строки по группировкам отчёта сама.
💡 В СКД этот путь проще и надёжнее ручного объединения: не нужно следить за порядком полей и заглушками.
Пошагово в конструкторе запроса
Если запрос собирается мышкой, порядок такой.
- Откройте конструктор запроса (в модуле — правой кнопкой по тексту запроса или через меню
Текст → Конструктор запроса). - На первой закладке выберите таблицы и поля первой части.
- Перейдите на закладку «Объединения/Псевдонимы». Добавьте второй запрос, у объединения снимите флаг «Без повторяющихся» — это и есть
ОБЪЕДИНИТЬ ВСЕ. - В нижней таблице той же закладки сопоставьте поля: строки — итоговые колонки, столбцы — части объединения. Где поля нет, выберите пустое значение или впишите выражение.
- Нажмите «ОК». Теперь нужно обернуть результат.
- Вариант с вложенным запросом: создайте новый запрос, в списке таблиц добавьте вложенный запрос и перенесите в его конструктор готовое объединение (проще всего — вставить текст).
- На закладке «Группировка» внешнего запроса перенесите измерения в группируемые поля, суммовые поля — в суммируемые с функцией
Сумма.
Закладка «Группировка» в конструкторе относится к той части объединения, которая сейчас выбрана. Переключитесь на внешний запрос, прежде чем настраивать итог.
Многие разработчики проще пишут вложенную обёртку руками: конструктор потом нормально открывает и редактирует такой текст.
Пример кода: остаток, оборот и количество документов в одной группировке
Задача: по каждой номенклатуре за период получить продажи, возвраты, сальдо и количество документов. Возвраты нужны отдельной колонкой, поэтому в каждой части заводим оба поля и ставим 0 там, где значения нет.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Движения.Номенклатура КАК Номенклатура,
| СУММА(Движения.Продано) КАК Продано,
| СУММА(Движения.Возвращено) КАК Возвращено,
| СУММА(Движения.Продано - Движения.Возвращено) КАК Сальдо,
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Движения.Документ) КАК ЧислоДокументов
|ИЗ
| (ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| Товары.Сумма КАК Продано,
| 0 КАК Возвращено,
| Товары.Ссылка КАК Документ
| ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
| ГДЕ
| Товары.Ссылка.Проведен
| И Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| Возвраты.Номенклатура,
| 0,
| Возвраты.Сумма,
| Возвраты.Ссылка
| ИЗ
| Документ.ВозвратТоваровОтПокупателя.Товары КАК Возвраты
| ГДЕ
| Возвраты.Ссылка.Проведен
| И Возвраты.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода) КАК Движения
|
|СГРУППИРОВАТЬ ПО
| Движения.Номенклатура
|
|УПОРЯДОЧИТЬ ПО
| Сальдо УБЫВ";
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДатаСеанса()));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДатаСеанса()));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(СтрШаблон("%1: продано %2, возвращено %3, сальдо %4, документов %5",
Выборка.Номенклатура,
Выборка.Продано,
Выборка.Возвращено,
Выборка.Сальдо,
Выборка.ЧислоДокументов));
КонецЦикла;
Что здесь важно:
- 🔹 Порядок полей во второй части повторяет первую: номенклатура, продано, возвращено, документ. Перепутаете
0иВозвраты.Суммаместами — возвраты молча попадут в продажи. - 🔹
0вместоNULL. С нулём выражениеПродано - Возвращенодаёт число. СNULLразность в каждой строке была быNULL, и сальдо получилось бы пустым. - 🔹
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...)считает уникальные документы по всему объединению — это тоже работает только во внешнем запросе. - 🔹
УПОРЯДОЧИТЬ ПО Сальдоссылается на псевдоним внешнего запроса и стоит в самом конце.
Проверка арифметики на простом примере. Товар продан двумя документами на 5 000 и 3 000 руб., один возврат на 1 000 руб. Результат: продано 8 000, возвращено 1 000, сальдо 7 000, документов 3.
Агрегатные функции после объединения
| Функция | Что даёт на объединённых данных |
|---|---|
СУММА | Сумма по всем частям; NULL пропускает |
КОЛИЧЕСТВО(*) | Число строк всех частей |
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле) | Число уникальных значений по всему набору |
МАКСИМУМ / МИНИМУМ | Например, дата последнего движения из любой части |
СРЕДНЕЕ | Среднее по всем строкам; заглушки 0 тоже участвуют и тянут среднее вниз |
Для СРЕДНЕЕ и МИНИМУМ заглушка 0 искажает результат. Если в части нет значения, а считать среднее или минимум нужно, ставьте NULL: агрегатные функции его игнорируют.
Как ускорить группировку большого объединения
Объединение по крупным табличным частям легко превращается в запрос на минуты. Что реально помогает:
ОБЪЕДИНИТЬ ВСЕвместоОБЪЕДИНИТЬ. Перед группировкой удаление дублей бесполезно и вредно: СУММА всё равно свернёт строки, а СУБД потратит время на поиск одинаковых.- Фильтры — внутрь каждой части. Условие на период и проведённость пишите в
ГДЕкаждой части, а не во внешнем запросе. Для виртуальных таблиц регистров (Остатки,Обороты) — в параметрах виртуальной таблицы, а не вГДЕ. - Предварительная группировка частей. Если в каждой части тысячи строк на одну номенклатуру, сверните их внутри части (
СУММА+СГРУППИРОВАТЬ ПО), а внешний запрос свернёт уже короткий набор. Двухуровневая группировка даёт тот же итог, если функция —СУММА,КОЛИЧЕСТВО(*)(во внешнем запросе — суммой),МАКСИМУМ,МИНИМУМ. ДляСРЕДНЕЕиКОЛИЧЕСТВО(РАЗЛИЧНЫЕ)так нельзя. - Временная таблица с индексом, если результат дальше соединяется с другими таблицами:
ИНДЕКСИРОВАТЬ ПО НоменклатурапослеПОМЕСТИТЬ. - Регистры вместо табличных частей документов. Обороты регистра накопления уже агрегированы платформой и обычно читаются быстрее, чем все строки документов.
- Меньше разыменований.
Товары.Ссылка.Датав каждой части — это соединение с шапкой документа. Нужные реквизиты лучше получить один раз, а не тянуть их через точку во внешнем запросе.
💡 Время выполнения сравнивайте в консоли запросов на копии рабочей базы с реальным объёмом. На демо-базе любой вариант выполняется мгновенно.
Ошибки при группировке объединения и как их исправить
СГРУППИРОВАТЬ ПО, а поле не перечислено в группировке и не обёрнуто в агрегатную функцию. При объединении проверьте, к какой части относится группировка: скорее всего, она стоит в конце и группирует только последний запрос. Вынесите группировку во внешний запрос.ВЫБРАТЬ и добавьте заглушку 0, NULL или пустую ссылку на нужное место.ОБЪЕДИНИТЬ без «ВСЕ». Две одинаковые строки из разных документов (та же номенклатура, та же сумма) склеились в одну. Замените на ОБЪЕДИНИТЬ ВСЕ.NULL вместо 0, и арифметика с ним дала NULL. Поставьте 0 или оберните поле в ЕСТЬNULL(Поле, 0).ВЫРАЗИТЬ(Поле КАК СТРОКА(150)) в каждой части объединения.Частые вопросы
Можно ли использовать ИТОГИ вместо вложенного запроса?
ИТОГИ СУММА(Сумма) ПО Номенклатура в конце объединения посчитает итоги по всем частям. Но детальные строки останутся в результате, итоговые записи добавятся к ним. Для отчёта с иерархией это удобно, для плоской таблицы — нет.Сколько частей можно объединить в одном запросе?
Как понять в итоговой таблице, из какой части пришла строка?
"Продажа" и "Возврат" под одним псевдонимом. По нему можно и группировать, и фильтровать.