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