СГРУППИРОВАТЬ ПО после ОБЪЕДИНИТЬ ВСЕ: один итог по всем частям запроса

· 9 мин

Содержание

Чтобы сгруппировать объединенные запросы в 1С, оберните весь блок ОБЪЕДИНИТЬ ВСЕ во вложенный запрос (или сложите его во временную таблицу через ПОМЕСТИТЬ) и уже во внешнем запросе напишите СГРУППИРОВАТЬ ПО с СУММА, КОЛИЧЕСТВО, МАКСИМУМ. Если поставить СГРУППИРОВАТЬ ПО просто в конце объединения, группировка сработает только для последней части.

Это правило одинаково для всех конфигураций на платформе 8.3: БП, ЗУП, УТ, ERP, самописных баз. Оно относится к языку запросов, а не к прикладному решению. Ниже — синтаксис, рабочий код и разбор ошибок, на которых спотыкаются даже опытные разработчики.

Как устроено объединение в языке запросов 1С

Оператор объединения склеивает результаты нескольких ВЫБРАТЬ в одну таблицу «по вертикали»: строки второго запроса дописываются под строками первого.

Есть два варианта:

ОператорЧто делает с дублямиСкоростьКогда брать
ОБЪЕДИНИТЬУбирает полностью одинаковые строкиМедленнее: СУБД ищет дублиНужен список уникальных значений
ОБЪЕДИНИТЬ ВСЕОставляет все строкиБыстрееПочти всегда, особенно перед группировкой

Правила, которые проверяет платформа:

  • 📐 Число полей во всех частях одинаковое. Не хватает поля — подставьте заглушку: 0, "", NULL, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка).
  • 🔢 Поля сопоставляются по порядку, а не по имени. Псевдонимы во второй и следующих частях ни на что не влияют.
  • 🏷️ Имена колонок итоговой таблицы берутся из первого запроса.
  • 🧩 Тип поля результата — объединение типов из всех частей. Если в первой части ссылка на договор, а во второй — на контрагента, поле станет составного типа.
  • 📌 УПОРЯДОЧИТЬ ПО, ИТОГИ и АВТОУПОРЯДОЧИВАНИЕ пишутся один раз, в самом конце, и действуют на весь результат.
  • 🧱 СГРУППИРОВАТЬ ПО, ИМЕЮЩИЕ, ГДЕ — свои у каждой части.

Последний пункт и создаёт главную путаницу.

Почему СГРУППИРОВАТЬ ПО в конце не группирует всё объединение

Посмотрите на типичную попытку получить продажи минус возвраты по номенклатуре:

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    Товары.Сумма КАК Сумма
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Возвраты.Номенклатура,
    -Возвраты.Сумма
ИЗ
    Документ.ВозвратТоваровОтПокупателя.Товары КАК Возвраты

СГРУППИРОВАТЬ ПО
    Возвраты.Номенклатура

Здесь СГРУППИРОВАТЬ ПО принадлежит второму ВЫБРАТЬ. Платформа сразу выдаст ошибку Поле не входит в группу: поле -Возвраты.Сумма не обёрнуто в агрегатную функцию.

Допустим, вы исправили это на СУММА(-Возвраты.Сумма). Ошибка уйдёт, но результат будет неправильным:

  • строки реализаций останутся построчными, по одной на каждую строку табличной части;
  • возвраты свернутся по номенклатуре;
  • одна и та же позиция появится в отчёте несколько раз.

Запрос, который выполнился без ошибки, ещё не значит, что он считает правильно. Проверяйте результат на номенклатуре, у которой есть и продажи, и возвраты.

Вывод простой: группировка по всему объединению делается на уровень выше. Для этого есть три способа.

Три способа свернуть результат объединения

СпособКогда подходитМинусы
Вложенный запрос в ИЗРазовая выборка, объём умеренныйДлинный вложенный текст хуже читается и отлаживается
Временная таблица ПОМЕСТИТЬПакетный запрос, результат нужен ещё раз, большой объёмБольше текста, нужен МенеджерВременныхТаблиц при нескольких запросах
Набор данных «Объединение» в СКДОтчёт на системе компоновкиТолько внутри СКД, не для кода обработки

Способ 1. Вложенный запрос

Весь блок с ОБЪЕДИНИТЬ ВСЕ помещается в скобки после ИЗ и получает псевдоним. Внешний запрос видит его как обычную таблицу:

ВЫБРАТЬ
    Движения.Номенклатура КАК Номенклатура,
    СУММА(Движения.Сумма) КАК Сумма
ИЗ
    (ВЫБРАТЬ
        Товары.Номенклатура КАК Номенклатура,
        Товары.Сумма КАК Сумма
    ИЗ
        Документ.РеализацияТоваровУслуг.Товары КАК Товары
    ГДЕ
        Товары.Ссылка.Проведен

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
        Возвраты.Номенклатура,
        -Возвраты.Сумма
    ИЗ
        Документ.ВозвратТоваровОтПокупателя.Товары КАК Возвраты
    ГДЕ
        Возвраты.Ссылка.Проведен) КАК Движения

СГРУППИРОВАТЬ ПО
    Движения.Номенклатура

Способ 2. Временная таблица

ПОМЕСТИТЬ пишется в первой части объединения и забирает в таблицу результат всех частей сразу:

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    Товары.Сумма КАК Сумма
ПОМЕСТИТЬ ВТ_Движения
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
    Товары.Ссылка.Проведен

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    Возвраты.Номенклатура,
    -Возвраты.Сумма
ИЗ
    Документ.ВозвратТоваровОтПокупателя.Товары КАК Возвраты
ГДЕ
    Возвраты.Ссылка.Проведен
;

////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Движения.Номенклатура КАК Номенклатура,
    СУММА(ВТ_Движения.Сумма) КАК Сумма
ИЗ
    ВТ_Движения КАК ВТ_Движения

СГРУППИРОВАТЬ ПО
    ВТ_Движения.Номенклатура

Это пакет из двух запросов, разделённых ;. Результат возвращает последний.

Способ 3. Объединение в СКД

В схеме компоновки данных добавьте набор данных типа «Объединение» и внутри него — два набора-запроса. Поля сопоставляются по имени, а не по порядку.

Сами запросы группировку не делают. Сумма отмечается как ресурс на закладке «Ресурсы», и компоновка свернёт строки по группировкам отчёта сама.

💡 В СКД этот путь проще и надёжнее ручного объединения: не нужно следить за порядком полей и заглушками.

Пошагово в конструкторе запроса

Если запрос собирается мышкой, порядок такой.

  1. Откройте конструктор запроса (в модуле — правой кнопкой по тексту запроса или через меню Текст → Конструктор запроса).
  2. На первой закладке выберите таблицы и поля первой части.
  3. Перейдите на закладку «Объединения/Псевдонимы». Добавьте второй запрос, у объединения снимите флаг «Без повторяющихся» — это и есть ОБЪЕДИНИТЬ ВСЕ.
  4. В нижней таблице той же закладки сопоставьте поля: строки — итоговые колонки, столбцы — части объединения. Где поля нет, выберите пустое значение или впишите выражение.
  5. Нажмите «ОК». Теперь нужно обернуть результат.
  6. Вариант с вложенным запросом: создайте новый запрос, в списке таблиц добавьте вложенный запрос и перенесите в его конструктор готовое объединение (проще всего — вставить текст).
  7. На закладке «Группировка» внешнего запроса перенесите измерения в группируемые поля, суммовые поля — в суммируемые с функцией Сумма.

Закладка «Группировка» в конструкторе относится к той части объединения, которая сейчас выбрана. Переключитесь на внешний запрос, прежде чем настраивать итог.

Многие разработчики проще пишут вложенную обёртку руками: конструктор потом нормально открывает и редактирует такой текст.

Пример кода: остаток, оборот и количество документов в одной группировке

Задача: по каждой номенклатуре за период получить продажи, возвраты, сальдо и количество документов. Возвраты нужны отдельной колонкой, поэтому в каждой части заводим оба поля и ставим 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: агрегатные функции его игнорируют.

Как ускорить группировку большого объединения

Объединение по крупным табличным частям легко превращается в запрос на минуты. Что реально помогает:

  1. ОБЪЕДИНИТЬ ВСЕ вместо ОБЪЕДИНИТЬ. Перед группировкой удаление дублей бесполезно и вредно: СУММА всё равно свернёт строки, а СУБД потратит время на поиск одинаковых.
  2. Фильтры — внутрь каждой части. Условие на период и проведённость пишите в ГДЕ каждой части, а не во внешнем запросе. Для виртуальных таблиц регистров (Остатки, Обороты) — в параметрах виртуальной таблицы, а не в ГДЕ.
  3. Предварительная группировка частей. Если в каждой части тысячи строк на одну номенклатуру, сверните их внутри части (СУММА + СГРУППИРОВАТЬ ПО), а внешний запрос свернёт уже короткий набор. Двухуровневая группировка даёт тот же итог, если функция — СУММА, КОЛИЧЕСТВО(*) (во внешнем запросе — суммой), МАКСИМУМ, МИНИМУМ. Для СРЕДНЕЕ и КОЛИЧЕСТВО(РАЗЛИЧНЫЕ) так нельзя.
  4. Временная таблица с индексом, если результат дальше соединяется с другими таблицами: ИНДЕКСИРОВАТЬ ПО Номенклатура после ПОМЕСТИТЬ.
  5. Регистры вместо табличных частей документов. Обороты регистра накопления уже агрегированы платформой и обычно читаются быстрее, чем все строки документов.
  6. Меньше разыменований. Товары.Ссылка.Дата в каждой части — это соединение с шапкой документа. Нужные реквизиты лучше получить один раз, а не тянуть их через точку во внешнем запросе.

💡 Время выполнения сравнивайте в консоли запросов на копии рабочей базы с реальным объёмом. На демо-базе любой вариант выполняется мгновенно.

Ошибки при группировке объединения и как их исправить

Диагностика: выберите симптом

Частые вопросы

Можно ли использовать ИТОГИ вместо вложенного запроса?
Да, ИТОГИ СУММА(Сумма) ПО Номенклатура в конце объединения посчитает итоги по всем частям. Но детальные строки останутся в результате, итоговые записи добавятся к ним. Для отчёта с иерархией это удобно, для плоской таблицы — нет.
Сколько частей можно объединить в одном запросе?
Практически столько, сколько нужно: в типовых конфигурациях встречаются объединения из десятка частей. Ограничивает скорее читаемость и время выполнения, чем синтаксис.
Как понять в итоговой таблице, из какой части пришла строка?
Добавьте в каждую часть поле-метку, например строку "Продажа" и "Возврат" под одним псевдонимом. По нему можно и группировать, и фильтровать.
Почему в объединении поле стало составного типа?
Тип поля результата собирается из типов всех частей. Если в одной части справочник, а в другой документ, поле получит оба типа. Это не мешает группировке, но при выводе проверяйте тип значения.
В СКД объединение группируется так же?
Нет, проще. Внутри СКД с набором «Объединение» группирует сама компоновка по ресурсам. В обычном тексте запроса правило одно: группировка по всему объединению — только во внешнем запросе или во временной таблице.