Как получить только группы справочника первого уровня в 1С 7.7?

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

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

Ограничения встроенного («чёрного») языка запросов 1С 7.7

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

  1. Группы не являются самостоятельными записями: в таблице результатов штатного запроса группа формируется исключительно как уровень группировки над детальными элементами справочника.
  2. Пропуск пустых папок: если группа первого уровня пуста (не содержит элементов) либо все вложенные в нее элементы исключены условиями отбора (например, помечены на удаление), такая группа физически не попадет в выборку запроса.
  3. Отсутствие функций определения типа и уровня: в языке запросов 1С 7.7 нет встроенных аналогов функций ЭтоГруппа() и Уровень(). В запросе невозможно синтаксически отфильтровать только группы.

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


Пустышка = ПолучитьПустоеЗначение("Справочник.Номенклатура");
Запрос = СоздатьОбъект("Запрос");

ТекстЗапроса = "
|Обрабатывать НеПомеченныеНаУдаление;
|Группа = Справочник.Номенклатура.Родитель;
|Условие(Группа.Родитель = Пустышка);
|Группировка Группа;";

Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    Пока Запрос.Группировка(1) = 1 Цикл
        Сообщить(Запрос.Группа.Наименование);
    КонецЦикла;
КонецЕсли;

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

Решение 1. Штатная объектная выборка (самый надежный и быстрый способ)

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

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

  1. Позиционирование на корень справочника: вызываем метод ИспользоватьРодителя("") или передаем пустое значение справочника, чтобы выборка началась с корневого уровня.
  2. Отключение спуска в подпапки: устанавливаем режим ВключатьПодчиненные(0). В этом случае выборка читает только непосредственные строки корня.
  3. Открытие выборки: вызываем метод ВыбратьЭлементы().
  4. Фильтрация только папок: так как в корне справочника могут находиться как папки, так и обычные элементы, внутри цикла проверяем условие Спр.ЭтоГруппа() = 1.
  5. Контроль пометки на удаление: если удаленные папки не нужны, дополнительно проверяем метод Спр.ПометкаУдаления() = 0.

Посмотрим на практическую реализацию этого метода в программном коде:


Спр = СоздатьОбъект("Справочник.Номенклатура");

// Устанавливаем отбор по корню дерева и отключаем обход подпапок
Спр.ИспользоватьРодителя("");
Спр.ВключатьПодчиненные(0);

// Открываем выборку элементов первого уровня
Спр.ВыбратьЭлементы();

Пока Спр.ПолучитьЭлемент() = 1 Цикл
    // Проверяем, что текущая запись является группой и не помечена на удаление
    Если (Спр.ЭтоГруппа() = 1) И (Спр.ПометкаУдаления() = 0) Тогда
        Сообщить("Найдена корневая группа: " + Спр.Наименование + " [Код: " + Спр.Код + "]");
    КонецЕсли;
КонецЦикла;

Такой подход выполняется платформой практически мгновенно, гарантированно возвращает даже пустые группы и не требует сторонних компонент.

Решение 2. Выгрузка в таблицу значений для дальнейшей обработки

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


ТЗГруппы = СоздатьОбъект("ТаблицаЗначений");
ТЗГруппы.НоваяКолонка("ТекущийЭлемент", "Справочник.Номенклатура");
ТЗГруппы.НоваяКолонка("Код", "Строка");
ТЗГруппы.НоваяКолонка("Наименование", "Строка");

Спр = СоздатьОбъект("Справочник.Номенклатура");
Спр.ИспользоватьРодителя("");
Спр.ВключатьПодчиненные(0);
Спр.ВыбратьЭлементы();

Пока Спр.ПолучитьЭлемент() = 1 Цикл
    Если (Спр.ЭтоГруппа() = 1) И (Спр.ПометкаУдаления() = 0) Тогда
        ТЗГруппы.НоваяСтрока();
        ТЗГруппы.ТекущийЭлемент = Спр.ТекущийЭлемент();
        ТЗГруппы.Код            = Спр.Код;
        ТЗГруппы.Наименование   = Спр.Наименование;
    КонецЕсли;
КонецЦикла;

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

Решение 3. Прямой SQL-запрос к базе данных

В конфигурациях, работающих на SQL-версии платформы с использованием внешних компонент прямого доступа к данным (таких как 1sqlite, класс ODBCRecordset из состава ВК 1S++), задачу можно решить напрямую через базу данных. Это решение извлекает папки первого уровня одним запросом.

Проанализируем структуру хранения иерархии в таблицах 1С 7.7:

  1. Поле ISFOLDER: отвечает за тип записи. Значение 1 соответствует группе справочника, значение 2 — конечному элементу.
  2. Поле PARENTID: хранит идентификатор вышестоящей папки. Для корневых элементов и папок первого уровня это поле содержит пустой идентификатор (в базе MS SQL и DBF это строка фиксированной длины из 9 пробелов: ' ').
  3. Поле ISMARK: логический флаг пометки на удаление (0 — не помечен, 1 — помечен на удаление).

Посмотрим на пример прямого SQL-запроса:


SELECT 
    Номенклатура.ID AS [Элемент $Справочник.Номенклатура],
    Номенклатура.CODE AS Код,
    Номенклатура.DESCR AS Наименование
FROM 
    $Справочник.Номенклатура AS Номенклатура WITH (NOLOCK)
WHERE 
    Номенклатура.ISFOLDER = 1
    AND Номенклатура.PARENTID = $ПустойИд
    AND Номенклатура.ISMARK = 0
ORDER BY 
    Номенклатура.DESCR

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

Итоги и рекомендации

Выясним причину выбора конкретного решения в реальной практике:

  1. Штатный «чёрный» запрос 1С 7.7 для этой задачи непригоден, так как спроектирован для вычисления агрегатов над элементами и игнорирует пустые папки.
  2. Обход через ВключатьПодчиненные(0) и ЭтоГруппа() = 1 является стандартным, универсальным и высокоскоростным решением для файловых (DBF) и клиент-серверных (SQL) баз без необходимости установки внешних компонент.
  3. Прямой SQL-запрос — наиболее эффективный вариант при построении сложных отчетов на тяжелых базах данных под управлением MS SQL Server.
← На главную