Как в запросе 1С получить только итоговые значения без детальных записей

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с запросами в системе 1С:Предприятие часто возникает задача получить не весь список записей из базы данных, а только агрегированные показатели — например, общую сумму начислений по каждому сотруднику. Новички часто путают использование конструкций ИТОГИ и СГРУППИРОВАТЬ ПО, что приводит к избыточности данных в результате запроса. В этой статье мы подробно разберем, как правильно составить запрос, чтобы оставить в результате только итоги, и как отсечь пустые значения.

Способ 1. Использование конструкции СГРУППИРОВАТЬ ПО

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

Разберем ситуацию на примере. Допустим, нам нужно получить суммы по сотрудникам из регистра расчета Начисления. Мы должны перечислить поля, по которым группируем, и применить агрегатные функции (такие как СУММА, МАКСИМУМ, КОЛИЧЕСТВО) к ресурсным полям.

Рассмотрим пример кода:


ВЫБРАТЬ
    Начисления.Сотрудник КАК Сотрудник,
    СУММА(Начисления.Результат) КАК Результат
ИЗ
    РегистрРасчета.Начисления КАК Начисления
ГДЕ
    Начисления.ПериодРегистрации = &ПериодРегистрации
    И (Начисления.ВидРасчета = &ВидРасчета1 ИЛИ Начисления.ВидРасчета = &ВидРасчета2)
СГРУППИРОВАТЬ ПО
    Начисления.Сотрудник

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

Фильтрация итоговых значений с помощью ИМЕЮЩИЕ

Проанализируем ситуацию, когда в результат группировки попадают строки с нулевыми суммами. Обычное условие ГДЕ здесь не поможет, так как оно фильтрует исходные записи до того, как они будут просуммированы. Для фильтрации уже рассчитанных агрегатов используется конструкция ИМЕЮЩИЕ.

Добавим в наш запрос условие, чтобы исключить сотрудников с нулевым результатом:


ВЫБРАТЬ
    Начисления.Сотрудник КАК Сотрудник,
    СУММА(Начисления.Результат) КАК Результат
ИЗ
    РегистрРасчета.Начисления КАК Начисления
ГДЕ
    Начисления.ПериодРегистрации = &ПериодРегистрации
СГРУППИРОВАТЬ ПО
    Начисления.Сотрудник
ИМЕЮЩИЕ
    СУММА(Начисления.Результат) <> 0

Выясним причину, почему это эффективно: СУБД сначала выполнит фильтрацию по ГДЕ, затем сгруппирует данные и только в самом конце отсечет те группы, которые не удовлетворяют условию ИМЕЮЩИЕ. Это позволяет минимизировать объем передаваемых данных между сервером базы данных и сервером 1С.

Способ 2. Программный обход итогов (конструкция ИТОГИ)

Бывают случаи, когда в одном запросе нам нужны и детальные записи, и итоги по ним. Конструкция ИТОГИ строит дерево данных. Если мы выгрузим такой запрос в ТаблицаЗначений простым методом Выгрузить(), мы получим «кашу» из детальных записей и итоговых строк.

Чтобы программно выбрать только итоги, сохранив в тексте запроса структуру с ИТОГИ, мы должны использовать специальный метод обхода результата. Посмотрим на пример кода обработки:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Начисления.Сотрудник КАК Сотрудник,
    |   Начисления.Результат КАК Результат
    |ИЗ
    |   РегистрРасчета.Начисления КАК Начисления
    |ИТОГИ СУММА(Результат) ПО Сотрудник";

РезультатЗапроса = Запрос.Выполнить();

// Используем обход по группировкам, чтобы получить только верхний уровень (итоги)
ВыборкаСотрудник = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока ВыборкаСотрудник.Следующий() Цикл
    // Здесь мы работаем только с итоговыми строками по каждому сотруднику
    Сообщить("Сотрудник: " + ВыборкаСотрудник.Сотрудник + " Сумма: " + ВыборкаСотрудник.Результат);
    
    // Если бы нам нужны были детальные записи внутри сотрудника, 
    // мы бы вызвали ВыборкаСотрудник.Выбрать() здесь.
КонецЦикла;

Этот метод незаменим, когда в отчете требуется иерархия (например, итоги по Подразделениям, а внутри — по Сотрудникам). Для практической реализации подобного сценария подойдёт обработка для вывода результата запроса в дерево значений.

Оптимизация и технические нюансы

Разберем несколько профессиональных советов, которые помогут сделать ваши запросы быстрее и надежнее:

  1. Избегайте обращений через точку в условиях: В исходном вопросе использовался фильтр Начисления.ВидРасчета.Код = &Код. Это «плохой тон», так как 1С вынуждена создавать неявное левое соединение с таблицей справочника видов расчета. Правильнее передавать в параметры готовую ссылку: Начисления.ВидРасчета = &СсылкаНаВидРасчета.
  2. Обработка NULL: Если вы используете левые соединения, сумма может вернуть NULL. Чтобы избежать проблем при сравнении в ИМЕЮЩИЕ, используйте функцию ЕСТЬNULL(СУММА(Поле), 0).
  3. Временные таблицы: Если итоговые данные нужны для дальнейших расчетов в этом же запросе, лучше использовать ПОМЕСТИТЬ ВтИтоги. Это позволит индексно обрабатывать сгруппированные данные в последующих пакетах.
  4. Виртуальные таблицы: Помните, что для регистров накопления получение итогов через физическую таблицу — это грубая ошибка. Всегда используйте виртуальные таблицы .Обороты или .ОстаткиИОбороты, так как они работают с уже предрассчитанными итогами в базе данных.

Таким образом, выбор метода зависит от вашей конечной цели: для простой таблицы используйте СГРУППИРОВАТЬ ПО, а для сложной иерархической обработки — ИТОГИ с соответствующим типом обхода в коде 1С.

← На главную