Как получить нижний уровень иерархического списка в запросе 1С

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет IT и автоматизация бизнеса
← На главную

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

Метод левого соединения: Поиск отсутствующего родителя

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

Давайте рассмотрим структуру запроса, предложенную в обсуждении. Мы будем использовать ЛЕВОЕ СОЕДИНЕНИЕ таблицы самой с собой. В левой части соединения будет основная таблица, из которой мы хотим получить данные, а в правой — та же самая таблица, но выступающая в роли «проверки на наличие детей».


ВЫБРАТЬ
    Хозрасчетный.Ссылка КАК Ссылка
ИЗ
    ПланСчетов.Хозрасчетный КАК Хозрасчетный
        ЛЕВОЕ СОЕДИНЕНИЕ ПланСчетов.Хозрасчетный КАК Хозрасчетный1
        ПО Хозрасчетный.Ссылка = Хозрасчетный1.Родитель
ГДЕ
    Хозрасчетный1.Ссылка ЕСТЬ NULL

Проанализируем этот код по шагам:

  1. Выбор таблиц: Мы дважды обращаемся к таблице ПланСчетов.Хозрасчетный. Чтобы система их различала, мы присваиваем им псевдонимы (алиасы). Первая таблица — основной источник, вторая — вспомогательный.
  2. Условие соединения: Мы связываем Ссылка из основной таблицы с полем Родитель во вспомогательной таблице.
  3. Фильтрация: Если у счета из левой таблицы есть хотя бы один подчиненный счет, то во вспомогательной таблице найдется запись, и поле Хозрасчетный1.Ссылка будет заполнено. Если же подчиненных элементов нет, соединение вернет пустое значение. Условие ГДЕ Хозрасчетный1.Ссылка ЕСТЬ NULL как раз отсекает все счета, у которых есть «дети», оставляя только нижний уровень.

Особенности работы с Планом счетов

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

Если нам необходимо исключить счета верхнего уровня (корневые счета), которые не имеют родителей (например, счет «70»), и оставить только конкретные субсчета (70.01, 70.01.01), мы добавим проверку на пустую ссылку родителя:


ВЫБРАТЬ
    ПланСчетовУПП.Ссылка КАК Счет
ИЗ
    ПланСчетов.Хозрасчетный КАК ПланСчетовУПП
        ЛЕВОЕ СОЕДИНЕНИЕ ПланСчетов.Хозрасчетный КАК ПодчиненныеСчета
        ПО ПланСчетовУПП.Ссылка = ПодчиненныеСчета.Родитель
ГДЕ
    ПодчиненныеСчета.Ссылка ЕСТЬ NULL
    И НЕ ПланСчетовУПП.Родитель = ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ПустаяСсылка)

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

Альтернативный подход: Оператор НЕ В (NOT IN)

Хотя левое соединение считается более производительным на большинстве СУБД (таких как MS SQL Server), иногда для наглядности кода используют вложенный запрос с оператором НЕ В. Разберем, как это выглядит:


ВЫБРАТЬ
    Счета.Ссылка
ИЗ
    ПланСчетов.Хозрасчетный КАК Счета
ГДЕ
    Счета.Ссылка НЕ В (
        ВЫБРАТЬ РАЗЛИЧНЫЕ
            ВспСчета.Родитель
        ИЗ
            ПланСчетов.Хозрасчетный КАК ВспСчета
        ГДЕ
            ВспСчета.Родитель <> ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ПустаяСсылка)
    )

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

Применение в справочниках: реквизит "ЭтоГруппа"

В отличие от планов счетов, в иерархических справочниках 1С существует стандартный реквизит ЭтоГруппа. Казалось бы, достаточно просто отобрать элементы, где ЭтоГруппа = Ложь. Однако это не всегда решает задачу получения именно нижнего уровня. Почему?

Рассмотрим ситуацию: у нас есть группа «Спецодежда», внутри неё группа «Зимняя», а в ней — элементы (куртки). Если мы просто выберем элементы, мы получим куртки. Но что, если группа «Летняя» пуста? Метод левого соединения позволит нам найти пустые группы, которые фактически являются «тупиковыми» ветками иерархии, если это требуется бизнес-логикой. Если же нам нужны только конечные элементы-данные, мы комбинируем условия:


ВЫБРАТЬ
    Номенклатура.Ссылка
ИЗ
    Справочник.Номенклатура КАК Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Подчиненные
        ПО Номенклатура.Ссылка = Подчиненные.Родитель
ГДЕ
    Подчиненные.Ссылка ЕСТЬ NULL
    И Номенклатура.ЭтоГруппа = ЛОЖЬ

Оптимизация производительности: Временные таблицы

Если иерархический список очень велик (например, справочник номенклатуры в крупном ритейле), выполнение соединения «таблица к таблице» может нагрузить сервер. В таких случаях мы рекомендуем использовать временные таблицы для предварительного сбора всех родителей.

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

  1. Выбираем все уникальные ссылки родителей во временную таблицу.
  2. Индексируем временную таблицу по полю Родитель.
  3. Выполняем левое соединение основной таблицы с этой временной таблицей.

// Шаг 1: Получаем список всех родителей
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ПланСчетов.Родитель КАК Родитель
ПОМЕСТИТЬ ВтРодители
ИЗ
    ПланСчетов.Хозрасчетный КАК ПланСчетов
ГДЕ
    ПланСчетов.Родитель <> ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.ПустаяСсылка)
ИНДЕКСИРОВАТЬ ПО
    Родитель
;

// Шаг 2: Получаем листовые узлы
ВЫБРАТЬ
    ПланСчетов.Ссылка
ИЗ
    ПланСчетов.Хозрасчетный КАК ПланСчетов
        ЛЕВОЕ СОЕДИНЕНИЕ ВтРодители КАК ВтРодители
        ПО ПланСчетов.Ссылка = ВтРодители.Родитель
ГДЕ
    ВтРодители.Родитель ЕСТЬ NULL

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

Резюме

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

← На главную