Как установить фильтр по уровню справочника в запросе 1С 7.7?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Разберем основные способы решения этой задачи, их преимущества, особенности расчета итогов и влияние на производительность.

Способ 1. Использование пользовательской функции в секции Условие

Платформа 1С 7.7 позволяет вызывать функции модуля непосредственно внутри секции Условие(...) текста запроса. В такой функции можно проверить уровень переданного объекта с помощью стандартного метода Уровень().

Рассмотрим пример реализации:


Функция ПроверитьУровень(ТекЭлемент) Экспорт
    Если ТекЭлемент.Уровень() = 3 Тогда
        Возврат 1;
    Иначе
        Возврат 0;
    КонецЕсли;
КонецФункции

Процедура СформироватьЗапрос()
    Перем Запрос, ТекстЗапроса;
    Запрос = СоздатьОбъект("Запрос");
    ТекстЗапроса = "
    |Номенклатура = Справочник.Номенклатура.ТекущийЭлемент;
    |Группировка Номенклатура без групп;
    |Условие (ПроверитьУровень(Номенклатура) = 1);
    |";
    
    Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
        Возврат;
    КонецЕсли;
КонецПроцедуры

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

Влияние на скорость: вызов внешних функций в секции Условие отключает внутреннюю оптимизацию движка 1С 7.7. Платформа будет последовательно вызывать интерпретируемый код для каждой строки выборки, что на больших объемах данных может замедлить формирование отчета.

Способ 2. Предварительный сбор элементов в СписокЗначений

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

Разберем данный подход по шагам:

  1. Выполняем быстрый перебор справочника выборкой или вспомогательным запросом.
  2. Проверяем метод Уровень() и добавляем подходящие элементы в список.
  3. Используем полученный список в основном запросе.

Посмотрим на пример кода:


СписокУровня = СоздатьОбъект("СписокЗначений");
Спр = СоздатьОбъект("Справочник.Номенклатура");
Спр.ВыбратьЭлементы();
Пока Спр.ПолучитьЭлемент() = 1 Цикл
    Если Спр.Уровень() = 3 Тогда
        СписокУровня.ДобавитьЗначение(Спр.ТекущийЭлемент());
    КонецЕсли;
КонецЦикла;

Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "
|Товар = Регистр.ОстаткиТоваров.Товар;
|Остаток = Регистр.ОстаткиТоваров.ОстатокТовара;
|Функция КонОст = КонОст(Остаток);
|Группировка Товар без групп;
|Условие (Товар в СписокУровня);
|";

Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    // Обработка результата
КонецЕсли;

Способ 3. Фильтрация при обходе результатов без искажения итогов

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

Проанализируем пример обхода:


Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = "
|Товар = Регистр.ОстаткиТоваров.Товар;
|Количество = Регистр.ОстаткиТоваров.Количество;
|Функция СуммаКол = Сумма(Количество);
|Группировка Товар;
|";

Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    Пока Запрос.Группировка(1) = 1 Цикл
        // Метод Уровень() возвращает глубину группировки в дереве запроса
        Если Запрос.Уровень() = 3 Тогда
            // Выводим только секцию третьего уровня
            Таб.ВывестиСекцию("Строка3");
        ИначеЕсли Запрос.ЭтоГруппа = 1 Тогда
            // Сохраняем вывод общих итогов по вышестоящим папкам
            Таб.ВывестиСекцию("ЗаголовокГруппы");
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

Также для разделения агрегации и фильтрации можно выгрузить результат запроса методом Запрос.Выгрузить(ТЗ) в объект ТаблицаЗначений и далее обработать строки программно.

Способ 4. Добавление реквизита в метаданные

Для высоконагруженных отчетов и часто используемых фильтров наиболее производительным решением является добавление числового реквизита (например, УровеньВложенности) непосредственно в справочник. Заполнение реквизита настраивается в модуле формы или предопределенной процедуре ПриЗаписи():


Процедура ПриЗаписи()
    УровеньВложенности = Уровень();
КонецПроцедуры

Это позволяет строить условия вида Условие(Номенклатура.УровеньВложенности = 3); без снижения скорости работы и без вызова внешних функций.

← На главную