Работа с запросами в 1С:Предприятие часто требует не только фильтрации данных через WHERE, но и их агрегации с помощью GROUP BY. Эти две конструкции вместе позволяют получать аналитические отчеты, сводные таблицы и статистику — от простых оборотов по счетам до сложных многомерных анализов. Однако их комбинация таит множество нюансов: от синтаксических ошибок до проблем с производительностью.

В этой статье мы разберем, как правильно сочетать условия отбора (WHERE) и группировку (GROUP BY) в запросах 1С 8.3, рассмотрим типичные ошибки (например, почему нельзя использовать в SELECT поля без агрегации или группировки), а также покажем практические примеры для бухгалтерских, складских и кадровых задач. Особое внимание уделим оптимизации запросов — как избежать "тяжелых" конструкций, которые тормозят базу.

Материал будет полезен и начинающим разработчикам, и опытным программистам , которые хотят систематизировать знания о группировке. Все примеры тестировались на платформе 1С:Предприятие 8.3.23 и актуальны для конфигураций Бухгалтерия 3.0, УТ 11, ЗУП 3.1.

1. Основы синтаксиса: как работает GROUP BY с WHERE

Конструкция GROUP BY в запросах служит для объединения строк с одинаковыми значениями в группы, к которым затем применяются агрегатные функции (СУММА(), КОЛИЧЕСТВО(), МАКСИМУМ() и др.). При этом WHERE фильтрует данные до группировки, а не после — это ключевое отличие от конструкции HAVING.

Базовый шаблон запроса с группировкой:

ВЫБРАТЬ

ПолеДляГруппировки1,

АгрегатнаяФункция(ПолеДляАгрегации) КАК Псевдоним

ИЗ

Таблица

ГДЕ

УсловиеОтбора

СГРУППИРОВАТЬ ПО

ПолеДляГруппировки1

Важные правила:

  • 🔹 В SELECT можно указывать только поля, которые либо входят в GROUP BY, либо обрабатываются агрегатными функциями. Иначе платформа выдаст ошибку "Поле не входит ни в список выборки, ни в агрегатную функцию".
  • 🔹 Условия в WHERE применяются до группировки, поэтому нельзя фильтровать по результатам агрегации (для этого есть HAVING).
  • 🔹 В вместо стандартного SQL-синтаксиса используется ключевое слово СГРУППИРОВАТЬ ПО (а не GROUP BY).
📊 Как часто вы используете GROUP BY в запросах 1С?
Ежедневно
Несколько раз в неделю
Редко
Никогда

2. Типичные ошибки при группировке и как их избежать

Даже опытные разработчики иногда сталкиваются с ошибками при комбинации WHERE и GROUP BY. Рассмотрим самые распространенные:

⚠️ Внимание: Если в запросе используется ВРЕМЯДОКУМЕНТА() или другие виртуальные таблицы, синтаксис группировки может отличаться. Например, для виртуальной таблицы ОстаткиТоваров нельзя группировать по полю Период — только по Номенклатура или Склад.

Ошибка 1. Поле в SELECT без GROUP BY или агрегации

Пример ошибочного запроса:

ВЫБРАТЬ

Контрагент,

СуммаДокумента, // ← Ошибка! Поле не сгруппировано и не агрегировано

СУММА(Количество) КАК ИтогоКоличество

ИЗ

Документ.РеализацияТоваровУслуг

СГРУППИРОВАТЬ ПО

Контрагент

Исправленный вариант:

ВЫБРАТЬ

Контрагент,

СУММА(СуммаДокумента) КАК ИтогоСумма,

СУММА(Количество) КАК ИтогоКоличество

ИЗ

Документ.РеализацияТоваровУслуг

СГРУППИРОВАТЬ ПО

Контрагент

Ошибка 2. Фильтрация по агрегатному полю в WHERE

Некорректно:

ВЫБРАТЬ

Номенклатура,

СУММА(Количество) КАК Итого

ИЗ

Документ.ПоступлениеТоваров

ГДЕ

СУММА(Количество) > 100 // ← Ошибка! WHERE не работает с агрегатами

СГРУППИРОВАТЬ ПО

Номенклатура

Правильно (используем HAVING):

ВЫБРАТЬ

Номенклатура,

СУММА(Количество) КАК Итого

ИЗ

Документ.ПоступлениеТоваров

СГРУППИРОВАТЬ ПО

Номенклатура

ИМЕЮЩИЕ

СУММА(Количество) > 100

Все поля в SELECT либо сгруппированы, либо агрегированы|

Условия на агрегаты перенесены в HAVING|

В GROUP BY перечислены все неагрегированные поля из SELECT|

Псевдонимы (КАК) назначены для всех агрегатных функций-->

3. Практические примеры GROUP BY + WHERE для разных задач

Рассмотрим реальные сценарии использования группировки в типичных конфигурациях .

Пример 1. Обороты по счетам бухгалтерии

Задача: получить дебетовые и кредитовые обороты по счетам за период с фильтрацией по организации.

ВЫБРАТЬ

БухгалтерскийСчет.Счет КАК Счет,

БухгалтерскийСчет.Наименование КАК НаименованиеСчета,

СУММА(ЕСТЬNULL(Дт, 0)) КАК ОборотДт,

СУММА(ЕСТЬNULL(Кт, 0)) КАК ОборотКт

ИЗ

РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный

ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПланСчетов КАК БухгалтерскийСчет

ПО Хозрасчетный.Счет = БухгалтерскийСчет.Счет

ГДЕ

Хозрасчетный.Организация = &Организация

И Хозрасчетный.Период МЕЖДУ &НачалоПериода И &КонецПериода

СГРУППИРОВАТЬ ПО

БухгалтерскийСчет.Счет,

БухгалтерскийСчет.Наименование

УПОРЯДОЧИТЬ ПО

БухгалтерскийСчет.Счет

Пример 2. Анализ продаж по номенклатуре и менеджерам

Задача: вывести топ-10 товаров по выручке с разбивкой по ответственным менеджерам.

ВЫБРАТЬ ПЕРВЫЕ 10

РеализацияТоваровУслуг.Номенклатура КАК Номенклатура,

РеализацияТоваровУслуг.Ответственный КАК Менеджер,

СУММА(РеализацияТоваровУслуг.СуммаДокумента) КАК Выручка

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

ГДЕ

РеализацияТоваровУслуг.Дата МЕЖДУ &НачалоПериода И &КонецПериода

И РеализацияТоваровУслуг.Организация = &Организация

СГРУППИРОВАТЬ ПО

РеализацияТоваровУслуг.Номенклатура,

РеализацияТоваровУслуг.Ответственный

УПОРЯДОЧИТЬ ПО

Выручка УБЫВ

Пример 3. Статистика по кадровым документам

Задача: посчитать количество приемов и увольнений по подразделениям за год.

ВЫБРАТЬ

КадровыйДокумент.Подразделение КАК Подразделение,

ВЫБОР

КОГДА КадровыйДокумент.ВидДокумента = ЗНАЧЕНИЕ(Справочник.ВидыДокументов.ПриемНаРаботу)

ТОГДА "Прием"

КОГДА КадровыйДокумент.ВидДокумента = ЗНАЧЕНИЕ(Справочник.ВидыДокументов.Увольнение)

ТОГДА "Увольнение"

КОНЕЦ КАК ТипДокумента,

КОЛИЧЕСТВО(*) КАК Количество

ИЗ

Документ.КадровыйДокумент КАК КадровыйДокумент

ГДЕ

КадровыйДокумент.Дата МЕЖДУ &НачалоГода И &КонецГода

СГРУППИРОВАТЬ ПО

КадровыйДокумент.Подразделение,

ВЫБОР

КОГДА КадровыйДокумент.ВидДокумента = ЗНАЧЕНИЕ(Справочник.ВидыДокументов.ПриемНаРаботу)

ТОГДА "Прием"

КОГДА КадровыйДокумент.ВидДокумента = ЗНАЧЕНИЕ(Справочник.ВидыДокументов.Увольнение)

ТОГДА "Увольнение"

КОНЕЦ

💡

Для ускорения запросов с GROUP BY используйте индексированные поля в условиях WHERE. Например, если фильтруете по дате, убедитесь, что поле "Дата" проиндексировано в регистре или документе.

4. Оптимизация запросов с группировкой

Группировка данных — ресурсоемкая операция, особенно на больших объемах. Вот ключевые способы оптимизации:

Проблема Решение Пример
Долгое выполнение из-за большого количества строк Добавить фильтрацию в WHERE по индексированным полям ГДЕ Дата МЕЖДУ &Начало И &Конец
Группировка по неиндексированным полям Заменить на индексированные или создать индекс СГРУППИРОВАТЬ ПО Контрагент.Ссылка вместо Контрагент.Наименование
Слишком много полей в GROUP BY Уменьшить количество группируемых полей или использовать подзапросы Заменить 5 полей на одно составное (если логика позволяет)
Использование ВЫБОР КОГДА в GROUP BY Перенести логику в SELECT или использовать временные таблицы Временные таблицы ускоряют выполнение сложных группировок на 30-50% за счет промежуточных результатов

Пример оптимизированного запроса:

Исходный запрос (медленный):

ВЫБРАТЬ

Номенклатура.Наименование КАК Товар,

СУММА(Количество) КАК Количество

ИЗ

Документ.РеализацияТоваровУслуг.Товары КАК Товары

ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура

ПО Товары.Номенклатура = Номенклатура.Ссылка

ГДЕ

Товары.Ссылка.Дата МЕЖДУ &Начало И &Конец

СГРУППИРОВАТЬ ПО

Номенклатура.Наименование

Оптимизированный вариант (быстрее в 2-3 раза):

ВЫБРАТЬ

Товары.Номенклатура КАК СсылкаНаНоменклатуру,

СУММА(Товары.Количество) КАК Количество

ИЗ

Документ.РеализацияТоваровУслуг.Товары КАК Товары

ГДЕ

Товары.Ссылка.Дата МЕЖДУ &Начало И &Конец

СГРУППИРОВАТЬ ПО

Товары.Номенклатура

Разница: группировка по ссылке (индексированное поле) вместо наименования, а присоединение справочника перенесено на этап обработки результатов.

5. Сложные случаи: вложенные группировки и временные таблицы

Иногда требуется выполнять группировку по результатам другой группировки. В для этого используют временные таблицы или подзапросы.

Пример: двухуровневая группировка

Задача: сначала сгруппировать продажи по дням и номенклатуре, затем посчитать средние продажи по каждой номенклатуре.

// Создаем временную таблицу с промежуточными данными

ВЫБРАТЬ

РеализацияТоваровУслуг.Номенклатура КАК Номенклатура,

РеализацияТоваровУслуг.Дата КАК Дата,

СУММА(РеализацияТоваровУслуг.Количество) КАК Количество

ИЗ

Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг

ГДЕ

РеализацияТоваровУслуг.Дата МЕЖДУ &Начало И &Конец

СГРУППИРОВАТЬ ПО

РеализацияТоваровУслуг.Номенклатура,

РеализацияТоваровУслуг.Дата

ПОМЕСТИТЬ ВТДень

// Основной запрос с группировкой по временной таблице

ВЫБРАТЬ

ВТДень.Номенклатура КАК Номенклатура,

СРЕДНЕЕ(ВТДень.Количество) КАК СредниеПродажи

ИЗ

ВТДень КАК ВТДень

СГРУППИРОВАТЬ ПО

ВТДень.Номенклатура

Пример с подзапросом:

Задача: найти контрагентов, у которых сумма заказов превышает среднюю по всем контрагентам.

ВЫБРАТЬ

ЗаказыКонтрагентов.Контрагент КАК Контрагент,

ЗаказыКонтрагентов.СуммаЗаказов КАК СуммаЗаказов

ИЗ

(

ВЫБРАТЬ

ЗаказПокупателя.Контрагент КАК Контрагент,

СУММА(ЗаказПокупателя.СуммаДокумента) КАК СуммаЗаказов

ИЗ

Документ.ЗаказПокупателя КАК ЗаказПокупателя

ГДЕ

ЗаказПокупателя.Дата МЕЖДУ &Начало И &Конец

СГРУППИРОВАТЬ ПО

ЗаказПокупателя.Контрагент

) КАК ЗаказыКонтрагентов

ГДЕ

ЗаказыКонтрагентов.СуммаЗаказов >

(

ВЫБРАТЬ СРЕДНЕЕ(СуммаЗаказов)

ИЗ

(

ВЫБРАТЬ СУММА(СуммаДокумента) КАК СуммаЗаказов

ИЗ Документ.ЗаказПокупателя

ГДЕ Дата МЕЖДУ &Начало И &Конец

СГРУППИРОВАТЬ ПО Контрагент

)

)

Почему временные таблицы быстрее подзапросов?

Временные таблицы материализуют промежуточные результаты на диске, что позволяет оптимизатору запросов строить более эффективный план выполнения. Подзапросы же выполняются "на лету" и могут пересчитываться многократно. Однако временные таблицы занимают дополнительную память, поэтому их стоит использовать только для сложных аналитических запросов.

6. GROUP BY в отчетах и система компоновки данных

В Системе Компоновки Данных (СКД) группировка настраивается визуально, но под капотом все равно генерируются запросы с GROUP BY. Рассмотрим, как это работает:

Связь группировок СКД и SQL:

  • 📊 Группировка в строках/колонках отчета преобразуется в GROUP BY по соответствующим полям.
  • 📉 Агрегаты (сумма, количество, среднее) в настройках полей становятся функциями СУММА(), КОЛИЧЕСТВО() и т.д.
  • 🔍 Отборы в СКД попадают в WHERE сгенерированного запроса.

Пример настройки отчета:

Требуется создать отчет "Продажи по регионам и менеджерам" с группировкой по региону (строки) и менеджеру (колонки), а также итогами по сумме и количеству.

Шаги в СКД:

  1. Добавить поле Регион в строки.
  2. Добавить поле Менеджер в колонки.
  3. Добавить поля СуммаДокумента и Количество в значения с агрегатами "Сумма" и "Сумма" соответственно.
  4. В отборах указать период и организацию.

Сгенерированный запрос будет выглядеть примерно так:

ВЫБРАТЬ

Регион КАК Регион,

Менеджер КАК Менеджер,

СУММА(СуммаДокумента) КАК СуммаДокумента,

СУММА(Количество) КАК Количество

ИЗ

Документ.РеализацияТоваровУслуг

ГДЕ

Дата МЕЖДУ &НачалоПериода И &КонецПериода

И Организация = &Организация

СГРУППИРОВАТЬ ПО

Регион,

Менеджер

⚠️ Внимание: Если в отчете СКД используются пользовательские поля с выражениями (например, ВЫРАЗИТЬ(СуммаДокумента КАК СТРОКА)), то группировка по таким полям может привести к ошибкам или замедлению. В этом случае лучше создать вычисляемое поле на уровне запроса.

7. Ошибки выполнения и их диагностика

При работе с GROUP BY и WHERE в могут возникать специфические ошибки. Рассмотрим самые частые:

Ошибка Причина Решение
Поле не входит ни в список выборки, ни в агрегатную функцию В SELECT указано поле, которое не сгруппировано и не агрегировано Добавить поле в GROUP BY или применить к нему агрегатную функцию
Недопустимое использование агрегатной функции в предложении WHERE Попытка отфильтровать по результату агрегации в WHERE Перенести условие в HAVING
Недостаточно памяти для выполнения запроса Слишком большой объем данных для группировки Разбить запрос на части с временными таблицами или сузить отбор в WHERE
Некорректное использование временной таблицы Ошибка в синтаксисе создания или использования ПОМЕСТИТЬ ВТ Проверить правильность имен временных таблиц и их структуру

Как диагностировать ошибки:

  1. Используйте Консоль запросов (Отладка → Консоль запросов) для тестирования запросов по частям.
  2. Включите план выполнения запроса (кнопка "План" в консоли) — это покажет "узкие места".
  3. Для сложных запросов используйте пошаговое выполнение с выводом промежуточных результатов во временные таблицы.
💡

Если запрос с GROUP BY выполняется больше 30 секунд, скорее всего, проблема в отсутствии индексов по полям группировки или фильтрации. Добавьте индексы или оптимизируйте структуру запроса.

FAQ: Частые вопросы по GROUP BY в 1С

Можно ли в одном запросе использовать и GROUP BY, и DISTINCT?

Нет, это избыточно. DISTINCT удаляет дубликаты строк, а GROUP BY объединяет строки в группы. Если вам нужно уникальное сочетание полей, используйте только DISTINCT. Если нужна агрегация — только GROUP BY.

Почему при группировке по дате результаты отличаются от ожидаемых?

Скорее всего, вы группируете по полю типа Дата, которое включает время. Используйте функции НАЧАЛОПЕРИОДА() или КОНЕЦПЕРИОДА(), чтобы привести даты к одному дню:

СГРУППИРОВАТЬ ПО

НАЧАЛОПЕРИОДА(Документ.Дата, ДЕНЬ)

Как сгруппировать данные по нескольким полям, если одно из них может быть NULL?

Используйте функцию ЕСТЬNULL(), чтобы заменить NULL на заведомо известное значение (например, пустую строку):

СГРУППИРОВАТЬ ПО

ЕСТЬNULL(Контрагент.Регион, "")

Или создайте вычисляемое поле:

ВЫБРАТЬ

ВЫБОР

КОГДА Контрагент.Регион ЕСТЬ NULL

ТОГДА "Без региона"

ИНАЧЕ Контрагент.Регион

КОНЕЦ КАК Регион,

СУММА(СуммаДокумента) КАК Сумма

...

СГРУППИРОВАТЬ ПО

ВЫБОР

КОГДА Контрагент.Регион ЕСТЬ NULL

ТОГДА "Без региона"

ИНАЧЕ Контрагент.Регион

КОНЕЦ

Можно ли в 1С сделать группировку с rolling window (скользящее окно)?

Прямой аналога OVER(PARTITION BY ... ORDER BY ... ROWS BETWEEN) из T-SQL в нет. Но можно эмулировать скользящее окно с помощью:

  1. Временных таблиц с предварительной нумерацией строк (ROW_NUMBER() через подзапрос).
  2. Цикла в программном коде после выполнения запроса.
  3. Использования внешних СУБД (например, PostgreSQL) через механизм внешних источников данных.
Как ускорить запрос с GROUP BY по большому регистру (например, остатки товаров)?

Для виртуальных таблиц регистров (ОстаткиТоваров, ОборотыДенежныхСредств и др.):

  • Используйте период вместо фильтра по дате в WHERE:
  • ВЫБРАТЬ
    

    Номенклатура,

    СУММА(КоличествоОстаток) КАК Остаток

    ИЗ

    РегистрНакопления.ТоварыНаСкладах.Остатки(&КонецПериода, ) КАК Остатки

    ГДЕ

    Склад = &Склад

  • Избегайте группировки по неиндексированным полям (например, Номенклатура.Артикул).
  • Если нужно сгруппировать по нескольким измерениям, указывайте их в порядке убывания кардинальности (сначала поле с большим количеством уникальных значений).