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

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

При работе с запросами в платформе «1С:Предприятие» разработчики часто сталкиваются с необходимостью построения древовидных структур. Справочники со сложной иерархией (такие как «Номенклатура» или «Контрагенты») требуют красивого вывода, где группы (папки) и элементы визуально подчинены друг другу. Однако попытка одновременно рассчитать итоги по иерархии и отсортировать их стандартным образом часто приводит к ошибке компилятора запросов.

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

Выясняем причину: почему возникает ошибка СУБД?

Посмотрим на классическую ошибку, с которой сталкивается начинающий разработчик при написании запроса:


// Этот запрос вызовет синтаксическую ошибку!
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Номенклатура,
    |	СуммаОборот
    |ИЗ
    |	РегистрНакопления.Продажи.Обороты
    |УПОРЯДОЧИТЬ ПО
    |	Номенклатура ИЕРАРХИЯ
    |ИТОГИ ПО
    |	Номенклатура ИЕРАРХИЯ";

При попытке выполнить такой запрос платформа выдаст ошибку, сообщающую о невозможности совместного использования упорядочивания по иерархии и расчета иерархических итогов. Давайте проанализируем ситуацию и поймем логику работы СУБД.

Дело в том, что конструкции УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ и ИТОГИ ПО ... ИЕРАРХИЯ решают принципиально разные задачи и возвращают данные в абсолютно разных структурах:

  1. Секция УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ предназначена для получения плоской таблицы. В ней нет промежуточных суммовых итогов. Платформа просто выстраивает строки результата друг за другом так, чтобы элементы физически располагались под своими родительскими группами.
  2. Секция ИТОГИ ПО ... ИЕРАРХИЯ генерирует сложную древовидную структуру. В этой структуре итоговые строки по родительским группам физически отделены от строк детальных записей. Итоги содержат агрегированные данные (суммы, количества), рассчитанные по всей ветке иерархии.

Поскольку эти две конструкции формируют принципиально разные структуры данных на уровне СУБД, их одновременное использование логически конфликтует и синтаксически запрещено платформой.

Как платформа сортирует группы по умолчанию?

Выясним, что происходит, если мы используем иерархические итоги, но полностью убираем секцию УПОРЯДОЧИТЬ ПО:


ВЫБРАТЬ
    Номенклатура,
    СуммаОборот
ИЗ
    РегистрНакопления.Продажи.Обороты
ИТОГИ ПО
    Номенклатура ИЕРАРХИЯ

В таком сценарии платформа автоматически отсортирует результат по полям группировки. Но как именно? Внутри каждого уровня иерархии (внутри каждой родительской папки) элементы и вложенные группы будут упорядочены по своему внутреннему уникальному идентификатору — ссылке (UID в СУБД). Для конечного пользователя это выглядит как хаотичное нагромождение строк, не поддающееся никакой логике (не по алфавиту, не по коду и не по порядку создания).

Давайте рассмотрим эффективные практические способы решения этой проблемы и заставим дерево выстраиваться в нужном нам порядке.

Способ 1: Упорядочивание по ссылочному полю (без ключевого слова ИЕРАРХИЯ)

Самый простой и элегантный способ навести порядок на каждом уровне дерева — указать поле в секции УПОРЯДОЧИТЬ ПО, но исключить из него инструкцию ИЕРАРХИЯ. Рассмотрим пример:


ВЫБРАТЬ
    Номенклатура,
    СуммаОборот
ИЗ
    РегистрНакопления.Продажи.Обороты
УПОРЯДОЧИТЬ ПО
    Номенклатура
ИТОГИ ПО
    Номенклатура ИЕРАРХИЯ

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

Способ 2: Явное разделение папок и элементов (свойство ЭтоГруппа)

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

Чтобы решить эту задачу, мы добавим в сортировку булево свойство ЭтоГруппа. Разберем этот пример по шагам:


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

Разберем логику сортировки:

  1. Поле ЭтоГруппа имеет тип Булево. Значение Истина (группа) при сортировке по убыванию (УБЫВ) всегда будет находиться выше значения Ложь (элемент). Благодаря этому папки гарантированно поднимутся наверх списка на каждом уровне дерева.
  2. Вторым уровнем сортировки мы указываем Номенклатура.Наименование ВОЗР. Это обеспечит строгое алфавитное упорядочивание: сначала по алфавиту выстроятся все папки, а ниже них — все конечные элементы по алфавиту.

Способ 3: Использование автоупорядочивания

Платформа 1С предоставляет встроенный механизм автоматического определения правил сортировки. Мы можем использовать ключевое слово АВТОУПОРЯДОЧИВАНИЕ в самом конце запроса:


ВЫБРАТЬ
    Номенклатура,
    СуммаОборот
ИЗ
    РегистрНакопления.Продажи.Обороты
ИТОГИ ПО
    Номенклатура ИЕРАРХИЯ
АВТОУПОРЯДОЧИВАНИЕ

Преимущества: Этот метод очень прост в написании. Платформа самостоятельно анализирует метаданные справочника «Номенклатура», определяет его основное представление и автоматически размещает папки выше элементов, сортируя их должным образом.

Недостатки: Использовать этот метод на реальных высоконагруженных базах данных нужно с крайней осторожностью. Инструкция АВТОУПОРЯДОЧИВАНИЕ заставляет СУБД генерировать тяжелые SQL-запросы с большим количеством дополнительных полей для сортировки (включая сортировку по внутренним реквизитам и UID). Это может сильно снизить производительность при обработке больших массивов данных.

Способ 4: Исключение детальных записей (ТОЛЬКО ИЕРАРХИЯ)

Рассмотрим ситуацию, когда перед нами стоит задача выгрузить в Excel исключительно структуру категорий (папок) с рассчитанными по ним итогам, не выводя при этом сами конечные товары. Для этого мы можем использовать модификатор ТОЛЬКО ИЕРАРХИЯ:


ВЫБРАТЬ
    Номенклатура,
    СуммаОборот
ИЗ
    РегистрНакопления.Продажи.Обороты
УПОРЯДОЧИТЬ ПО
    Номенклатура
ИТОГИ ПО
    Номенклатура ТОЛЬКО ИЕРАРХИЯ

Этот запрос полностью исключит «листья» дерева (детальные элементы) и вернет только структуру групп с рассчитанными по ним агрегатными суммами, упорядоченную по алфавиту.

Обход результатов запроса в коде 1С

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


Процедура ВыгрузитьДеревоЗапроса()

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Номенклатура,
        |	Номенклатура.ЭтоГруппа КАК ЭтоГруппа,
        |	СуммаОборот КАК Сумма
        |ИЗ
        |	РегистрНакопления.Продажи.Обороты
        |УПОРЯДОЧИТЬ ПО
        |	ЭтоГруппа УБЫВ,
        |	Номенклатура.Наименование ВОЗР
        |ИТОГИ ПО
        |	Номенклатура ИЕРАРХИЯ";
        
    РезультатЗапроса = Запрос.Выполнить();
    
    // Используем специальный тип обхода для иерархии
    Выборка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
    
    ОбходитьВеткуРекурсивно(Выборка, 0);

КонецПроцедуры

Процедура ОбходитьВеткуРекурсивно(ВыборкаРодитель, Уровень)

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

КонецПроцедуры

Использование метода Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией) гарантирует, что 1С выведет данные в строгом соответствии с настроенными нами правилами сортировки, сохраняя красивое и упорядоченное дерево папок и элементов.

Рекомендуемая альтернатива: Использование Системы компоновки данных (СКД)

Если ваша конечная задача заключается в выводе данных в отчет, на форму или для последующего сохранения пользователем в файл Excel, то лучшим решением в современной разработке на платформе 1С будет полный отказ от использования секций ИТОГИ и УПОРЯДОЧИТЬ непосредственно в тексте запроса.

Вместо ручного написания итогов рекомендуется использовать Систему компоновки данных (СКД):

  1. Напишите простой «плоский» запрос без итогов и сортировок.
  2. В настройках СКД на вкладке «Настройки» добавьте группировку по полю Номенклатура.
  3. В типе группировки выберите значение «Иерархия» (или «Только иерархия» при необходимости).
  4. На вкладке «Сортировка» настройте упорядочивание по реквизитам.

СКД самостоятельно сгенерирует наиболее оптимальный SQL-запрос, автоматически поднимет папки наверх, отсортирует элементы по алфавиту и рассчитает итоги. Это решение работает быстрее, не нагружает СУБД лишними операциями и исключает любые ошибки синтаксиса запросов.

← На главную