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