Как в 1С 7.7 получить список групп справочника с помощью запроса

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

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

Почему штатный запрос 1С 7.7 не находит группы через условие

Проанализируем исходную конструкцию запроса, которая не дает результата:


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

Выясним причину ошибки в данном подходе. Механизм встроенного («черного») запроса 1С 7.7 имеет фундаментальную особенность: на этапе первичной выборки данных из таблиц базы данных запрос обращается исключительно к элементам (листьям дерева иерархии). Сами группы как самостоятельные физические записи в первичную выборку не попадают — они достраиваются механизмом запроса виртуально только при обходе иерархических группировок.

Кроме того, ЭтоГруппа() — это контекстный метод агрегатного объекта платформы, а не отдельный реквизит таблицы базы данных. Когда в секции Условие(...) проверяется выражение ТекущийЭлемент.ЭтоГруппа = 1, платформа вычисляет его для каждого перебираемого элемента. Поскольку перебираются только элементы, для них это значение всегда равно 0, и запрос отсекает абсолютно все строки выборки.

Способ 1. Получение непустых групп через группировку по Родителю

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

Посмотрим на пример правильного построения запроса:


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

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

Разберем важные нюансы этого метода:

  1. Мы убрали группировку Группировка ТекущийЭлемент, благодаря чему одинаковые родители сворачиваются в одну строку.
  2. В переменной Запрос.Родитель мы получаем полноценную ссылку на группу справочника, через которую доступны все стандартные реквизиты: наименование, код и реквизиты шапки.
  3. Главное ограничение: данный способ вернет только те группы, в которых есть элементы. Если группа создана, но пуста (в ней нет подчиненных элементов), запрос ее не увидит.

Способ 2. Быстрый перебор через объект «Справочник»

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

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


Спр = СоздатьОбъект("Справочник.Договора");
// Устанавливаем владельца для подчиненного справочника
Спр.ИспользоватьВладельца(ВыбВладелец);

// 0 — отключает автоматический рекурсивный вход в подчиненные группы
Спр.ВключатьПодчиненные(0); 

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

Если справочник имеет многоуровневую структуру и требуется обойти дерево групп целиком, напишем простую рекурсивную процедуру, которая обходит только папки:


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

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

Способ 3. Прямой SQL-запрос к базе данных (для SQL-версии 1С 7.7)

Когда информационная база работает под управлением Microsoft SQL Server, максимальную скорость работы обеспечивает выполнение прямого SQL-запроса (например, через внешнюю компоненту 1CPP или объект ADODB.Connection).

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

  1. ISFOLDER — признак типа записи: 1 — группа (папка), 2 — элемент.
  2. PARENTEXT — идентификатор владельца для подчиненных справочников.
  3. ISMARK — пометка на удаление (1 — помечен, 0 — не помечен).
  4. PARENTID — идентификатор родительской папки для иерархических справочников.

Посмотрим на пример прямого SQL-запроса к таблице справочника:


// Пример SQL-запроса с типизацией для 1С++
ТекстSQL = "
|SELECT 
|    ID as [Договор $Справочник.Договора],
|    CODE as [Код $Строка],
|    DESCR as [Наименование $Строка]
|FROM 
|    $Справочник.Договора (NOLOCK)
|WHERE 
|    PARENTEXT = :ВыбВладелец
|    AND ISFOLDER = 1
|    AND ISMARK = 0
|";

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

← На главную