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

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

При работе с запросами в 1С начинающие разработчики часто сталкиваются с ситуацией, когда при обходе результата по группировкам (иерархически) значения некоторых полей оказываются неопределенными (NULL). Рассмотрим типичную ситуацию: в запросе используются итоги по определенным полям, но нам нужно вытащить данные, которые не являются группировочными для текущего уровня и не обернуты в агрегатные функции. Разберем, как правильно организовать обход, чтобы получить доступ к каждой детали нашей выборки.

Анализ проблемы: почему данные не видны на уровне группировки

Проанализируем структуру запроса, предоставленного в примере. В тексте запроса указано предложение ИТОГИ по полям: ОБЩИЕ, Номенклатура, Спецификация, Склад. Поле Размер присутствует в секции ВЫБРАТЬ и в СГРУППИРОВАТЬ ПО, однако оно не указано в секции ИТОГИ ПО.

Когда мы используем метод РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "ИмяГруппировки"), система создает выборку, которая содержит только агрегированные данные (суммы, максимумы и т.д.) и само значение поля группировки. Все остальные поля, которые не входят в текущую группировку и не являются ресурсами, на данном этапе будут содержать NULL. Это фундаментальная особенность работы платформы 1С: на уровне группировки "Спецификация" данные о конкретных "Размерах" еще "свернуты", так как в рамках одной спецификации теоретически может быть несколько строк с разными размерами (если бы они не были сгруппированы ранее).

Решение задачи: использование детальной выборки

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

Рассмотрим правильный алгоритм обхода. После того как мы прошли по всем необходимым нам группировкам (Номенклатура -> Склад -> Спецификация), нам нужно создать еще одну выборку, вызвав метод Выбрать() без параметров или с параметром ОбходРезультатаЗапроса.Прямой.

Разберем программный код реализации:


// Получаем общие итоги (если они нужны)
ВыборкаОбщая = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаОбщая.Следующий() Цикл
    
    // Группировка по Номенклатуре
    ВыборкаПоАртикулу = ВыборкаОбщая.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Номенклатура");
    Пока ВыборкаПоАртикулу.Следующий() Цикл
        
        ОбластьСтрокаАртикул.Параметры.Заполнить(ВыборкаПоАртикулу);
        
        // Группировка по Складу
        ВыборкаПоСкладу = ВыборкаПоАртикулу.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Склад");
        Пока ВыборкаПоСкладу.Следующий() Цикл
            
            // Группировка по Спецификации
            ВыборкаПоСпецификации = ВыборкаПоСкладу.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Спецификация");
            Пока ВыборкаПоСпецификации.Следующий() Цикл
                
                // Чтобы получить Размер, делаем детальную выборку внутри последней группировки
                ВыборкаДетализация = ВыборкаПоСпецификации.Выбрать();
                
                Пока ВыборкаДетализация.Следующий() Цикл
                    // Теперь значение Размер доступно!
                    ТекРазмер = ВыборкаДетализация.Размер;
                    
                    // Заполняем параметры области детальными данными
                    ОбластьСтрокаСпецификация.Параметры.Заполнить(ВыборкаДетализация);
                    
                    // Здесь можно выводить строку в табличный документ
                КонецЦикла;
                
            КонецЦикла;    
        КонецЦикла;
    КонецЦикла;
КонецЦикла;

Альтернативный способ: использование агрегатных функций в итогах

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

Рассмотрим, как изменится текст запроса:


// ... (основной текст запроса)
ИТОГИ
    СУММА(КонечныйОстаток),
    МАКСИМУМ(Размер), // Добавляем поле Размер в итоги
    СУММА(Приход)
ПО
    ОБЩИЕ,
    Номенклатура,
    Спецификация,
    Склад

В этом случае поле Размер станет доступно напрямую в объекте ВыборкаПоСпецификации.Размер. Функции МАКСИМУМ и МИНИМУМ в 1С универсальны: они работают не только с числами, но и со строками, датами и ссылками. Это позволяет сократить вложенность циклов в коде 1С, что положительно сказывается на читаемости и производительности.

Важные нюансы иерархического обхода

Выясним некоторые технические особенности, которые помогут избежать ошибок в будущем:

  1. Порядок следования группировок: Метод Выбрать() крайне чувствителен к иерархии. Если в ИТОГИ ПО указано "Номенклатура, Склад", а в коде вы попытаетесь сначала выбрать "Склад", система может вернуть пустой результат. Всегда соблюдайте последовательность, описанную в запросе.
  2. Работа с иерархией справочников: Если вам нужно учитывать иерархию (группы и элементы справочника), используйте ОбходРезультатаЗапроса.ПоГруппировкамИИерархии. В противном случае "родительские" узлы будут проигнорированы.
  3. Выгрузка в дерево значений: Если программный обход кажется слишком громоздким, можно использовать метод РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам). Это создаст объект ДеревоЗначений, который повторяет структуру итогов запроса. В дереве доступ к любому полю строки возможен через коллекцию строк, что иногда удобнее для отладки.
  4. Производительность: Помните, что каждый вызов Выбрать() создает новый объект выборки. При обходе сотен тысяч записей с глубокой вложенностью это может потреблять значительный объем памяти. В таких случаях иногда эффективнее использовать ОбходРезультатаЗапроса.Прямой и отслеживать смену группировок вручную через сравнение текущего и предыдущего значения ключевого поля.

Таким образом, мы выяснили, что основной причиной отсутствия данных является попытка обращения к детальным полям на уровне итогов. Используя дополнительный обход через Выбрать() без параметров или включая поля в агрегатные функции итогов, вы сможете гибко управлять выводом данных в ваших отчетах и обработках.

← На главную