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

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

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

Выясняем причину дублирования групп

Проанализируем механизм работы конструкции ИТОГИ ПО ... ИЕРАРХИЯ. Когда мы указываем иерархические итоги, платформа формирует структуру данных, в которой каждая группа справочника выполняет две роли:

  1. Иерархический итог (узел дерева): Это служебная запись, которая служит «контейнером» для всех нижележащих элементов. Она необходима для расчета агрегатных функций (сумма, количество) по всей ветке иерархии.
  2. Детальная запись (объект данных): Если сама группа как объект справочника удовлетворяет условию в секции ГДЕ, она попадает в выборку как обычная строка данных.

Если мы используем плоский обход выборки или не проверяем тип записи, мы видим оба этих вхождения. Первое вхождение — это заголовок группы в структуре итогов, второе — сама группа как элемент данных внутри этой же группы. Рассмотрим ситуацию на примере: если у нас есть папка «Мебель», то в итогах она появится сначала как заголовок (тип записи ИтогПоИерархии), а затем внутри себя как элемент (тип записи ДетальнаяЗапись).

Оптимизация текста запроса: избавляемся от «лишних точек»

Прежде чем исправлять логику обхода, обратим внимание на качество самого запроса. В исходном примере кода часто встречается избыточное обращение через точку, например: Номенклатура.Ссылка.ЭтоГруппа или Номенклатура.Ссылка КАК Номенклатура.

Проанализируем, почему это плохо. Поле Номенклатура в секции ИЗ уже является ссылкой на таблицу справочника. Обращение Номенклатура.Ссылка заставляет систему выполнять лишнюю операцию получения ссылки, которая и так доступна. Еще более критично обращение Номенклатура.Ссылка.ЭтоГруппа. Каждая «точка» в запросе от ссылочного поля — это неявное левое соединение (LEFT JOIN) на уровне СУБД. Поскольку поле ЭтоГруппа содержится в основной таблице справочника Номенклатура, правильно обращаться к нему напрямую: Номенклатура.ЭтоГруппа.

Посмотрим на оптимизированный вариант запроса:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Ссылка,
    Номенклатура.ЭтоГруппа КАК ЭтоГруппа
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Родитель В ИЕРАРХИИ(&Папка)
ИТОГИ ПО
    Ссылка ИЕРАРХИЯ
АВТОУПОРЯДОЧИВАНИЕ

В данном примере мы убрали лишние обращения к Ссылка, что сделает SQL-запрос более легким и быстрым.

Способ 1: Использование проверки типа записи в выборке

Для того чтобы исключить повторный вывод группы, нам необходимо программно различать, является ли текущая строка выборки итогом по иерархии или детальной записью. Для этого у объекта выборки существует метод ТипЗаписи(). Рассмотрим пример правильного обхода выборки:


Выборка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

Пока Выборка.Следующий() Цикл
    // Проверяем тип записи
    Если Выборка.ТипЗаписи() = ТипЗаписиЗапроса.ИтогПоИерархии Тогда
        // Это иерархический "узел" (папка)
        Сообщить("Обработка группы (итог): " + Выборка.Ссылка);
        
        // Здесь мы можем запустить вложенный обход для получения элементов внутри этой группы
        ВыборкаДетальные = Выборка.Выбрать();
        Пока ВыборкаДетальные.Следующий() Цикл
            // Обработка вложенных элементов
        КонецЦикла;
        
    ИначеЕсли Выборка.ТипЗаписи() = ТипЗаписиЗапроса.ДетальнаяЗапись Тогда
        // Это обычный элемент или группа как объект, если она попала в выборку напрямую
        // Если мы не хотим выводить саму группу второй раз, здесь можно поставить условие
        Если Не Выборка.ЭтоГруппа Тогда
            Сообщить("Обработка элемента: " + Выборка.Ссылка);
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

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

Способ 2: Использование метода Выгрузить в Дерево Значений

Если целью запроса является визуальное отображение иерархии в табличном документе или на форме, зачастую проще не обходить выборку вручную, а воспользоваться методом выгрузки в дерево значений. Разберем, как это работает:


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

// Теперь в ДеревоРезультата у нас стройная структура без дублей групп,
// где группы являются строками-родителями, а элементы - их дочерними строками.

Метод ПоГруппировкамСИерархией автоматически схлопывает итоговые и детальные записи для групп, представляя структуру в максимально удобном для программиста виде. Это избавляет от необходимости писать сложные условия внутри циклов Пока ... Следующий().

Важные замечания по оператору В ИЕРАРХИИ

Выясним еще одну деталь, касающуюся условий в секции ГДЕ. Автор исходного вопроса использовал условие Номенклатура.Родитель В ИЕРАРХИИ(&Папка). Стоит помнить, что такое условие не включит в результат саму &Папка, а только ее содержимое. Если требуется включить и саму папку-родитель, следует писать Номенклатура.Ссылка В ИЕРАРХИИ(&Папка).

Подведем итоги: дублирование групп в запросах с итогами по иерархии — это не ошибка платформы, а особенность формирования итоговых записей. Для решения проблемы рекомендуется:

  1. Очистить запрос от лишних обращений через точку для повышения производительности.
  2. При обходе выборки использовать метод ТипЗаписи() для фильтрации итоговых и детальных строк.
  3. Для простых задач визуализации иерархии использовать выгрузку в ДеревоЗначений с параметром ПоГруппировкамСИерархией.

Эти подходы позволят создавать чистый, быстрый и эффективный код для работы со сложными структурами данных в 1С.

← На главную