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