В среде разработчиков «старой школы» 1С:Предприятия 7.7 существует устоявшийся термин «чёрный запрос». Это название закрепилось за стандартным механизмом платформы Запрос, так как текст запроса в конфигураторе по умолчанию отображался обычным чёрным шрифтом (в отличие от расцветок в более современных версиях). Одной из классических задач при работе со справочниками является получение списка только непосредственных родителей (ближайших групп) для выбранных элементов, исключая при этом всю вышестоящую иерархию. Разберем, как это реализовать эффективно и какие нюансы стоит учитывать.
Представим, что у нас есть многоуровневый справочник (например, «Номенклатура» или «Контрагенты»). Нам необходимо получить список всех групп, в которых непосредственно лежат выбранные элементы. Проблема заключается в том, что стандартный механизм иерархии в 1С 7.7 при обычном запросе стремится вывести полное дерево: если элемент находится в папке «Подкатегория», которая лежит в папке «Категория», запрос может вернуть обе эти группы. Наша же цель — получить только «Подкатегорию».
Проанализируем основные способы решения этой задачи, начиная от штатного запроса и заканчивая использованием прямых запросов к базе данных.
Для решения задачи в рамках штатных средств платформы нам потребуется правильно настроить секцию Группировка. Ключевым моментом здесь является использование параметра Без групп. Несмотря на название, этот параметр в контексте группировки по Родителю работает весьма специфично.
Рассмотрим пример кода, который формирует такой запрос:
// Создаем объект Запрос
ТекстЗапроса = "
|Период с '01.01.2023';
|ТекущийЭлемент = Справочник.Номенклатура.ТекущийЭлемент;
|РодительЭлемента = Справочник.Номенклатура.Родитель;
|Группировка РодительЭлемента Без групп;
|Условие (ТекущийЭлемент.ЭтоГруппа = 0);
|";
Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат;
КонецЕсли;
// Обходим результаты
Пока Запрос.Группировка(1) = 1 Цикл
// В Запрос.РодительЭлемента будет только непосредственный родитель
Сообщить("Найдена родительская группа: " + Запрос.РодительЭлемента);
КонецЦикла;
Разберем по шагам, почему это работает. Когда мы указываем Группировка РодительЭлемента Без групп;, платформа выполняет следующие действия:
Без групп отключает построение иерархических итогов в самом объекте Запрос. Это означает, что при обходе Запрос.Группировка(1) мы будем получать плоский список значений, которые попали в поле Родитель.Важное примечание: Если элемент находится в корне справочника, его Родитель будет пустым. Если вам не нужны пустые значения в списке, добавьте условие: Условие (ПустоеЗначение(РодительЭлемента) = 0);.
Иногда программисты пытаются решить задачу через получение путей. У справочников в 1С 7.7 есть строковые атрибуты ПолныйКод и ПолноеНаименование. В них иерархия разделена символом (обычно "/" или "."). Проанализируем этот подход.
Метод кажется простым: достаточно взять ПолныйКод, найти последний разделитель и получить родителя. Однако у этого способа есть критические недостатки:
ПолныйКод заставляет систему делать множество обращений к таблице базы данных для каждого уровня вложенности.Справочник, что потребует дополнительного поиска через НайтиПоКоду.Поэтому данный метод рекомендуется использовать только для визуального представления, но не для логики обработки данных.
Если ваша база работает на MS SQL Server и у вас установлена внешняя компонента 1С++, получение ближайших групп становится тривиальной задачей, которая выполняется мгновенно даже на миллионах записей. В структуре таблиц 1С 7.7 (в SQL это таблицы вида SC[ID_справочника]) за непосредственного родителя отвечает поле PARENTID.
Рассмотрим пример SQL-запроса через ODBCRecordset:
// Пример для 1С++
ЗапросSQL = СоздатьОбъект("ODBCRecordset");
ТекстSQL = "
|SELECT DISTINCT
| PARENTID as [Группа $Справочник.Номенклатура]
|FROM
| $Справочник.Номенклатура
|WHERE
| ISFOLDER = 2 -- Выбираем только элементы (не группы)
| AND PARENTID <> '0000000000' -- Исключаем корень
|";
ТЗ_Группы = ЗапросSQL.ВыполнитьИнструкцию(ТекстSQL);
В данном случае инструкция DISTINCT гарантирует нам получение уникального списка идентификаторов ближайших групп. Это самый быстрый способ из всех возможных, так как он обращается напрямую к индексам таблицы базы данных, минуя объектную модель 1С.
Если количество элементов невелико, или вам нужно собрать родителей в процессе выполнения какой-то другой логики, можно воспользоваться объектом СписокЗначений с проверкой на уникальность. Выясним, как это сделать правильно.
СписокГрупп = СоздатьОбъект("СписокЗначений");
Спр = СоздатьОбъект("Справочник.Номенклатура");
Спр.ВыбратьЭлементы();
Пока Спр.ПолучитьЭлемент() = 1 Цикл
Если Спр.ЭтоГруппа = 1 Тогда
Продолжить;
КонецЕсли;
ТекРодитель = Спр.Родитель;
Если ТекРодитель.Выбран() = 1 Тогда
// Используем поиск, чтобы не добавлять дубликаты
Если СписокГрупп.НайтиЗначение(ТекРодитель) = 0 Тогда
СписокГрупп.ДобавитьЗначение(ТекРодитель);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Этот метод прост в реализации, но медленнее запроса, так как требует полной выборки всех элементов справочника в память. Однако он незаменим, когда фильтрация элементов настолько сложна, что её невозможно описать в тексте штатного запроса.
Проанализировав все доступные инструменты 1С 7.7, можно сделать следующие выводы:
Группировка ... Без групп;. Это сохраняет типизацию данных и работает достаточно быстро.PARENTID.ПолныйКод для поиска объектов — это путь к ошибкам и замедлению работы системы.Помните, что в 1С 7.7 иерархия справочника жестко ограничена количеством уровней, заданным в конфигураторе. Штатный запрос всегда «знает» о структуре родительских связей, и правильное использование параметров группировки позволяет гибко управлять глубиной выборки данных без написания сложного кода.