Как найти подчиненные документы, если имя реквизита-основания заранее неизвестно?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки в среде «1С:Предприятие 8.3» программисты часто сталкиваются с задачей построения дерева подчиненности или поиска документов, созданных на основании конкретного объекта. Классический и самый простой сценарий — когда нам заранее известны все типы подчиненных документов и имена их реквизитов связи (например, стандартный реквизит ДокументОснование). В таком случае достаточно написать простой запрос к известным таблицам.

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

Метод 1. Универсальный платформенный поиск по ссылкам

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

Давайте разберем по шагам, как работает этот механизм. Метод осуществляет полнотекстовый поиск переданной ссылки (или массива ссылок) по всем индексированным полям таблиц базы данных СУБД. Это гарантирует, что связь будет найдена, даже если она записана в нетипичном месте.

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


// Создаем массив с одной ссылкой на наш документ-основание
МассивДокументов = Новый Массив;
МассивДокументов.Добавить(ОбъектОснованиеСсылка);

// Выполняем поиск ссылок по всей базе данных
ТаблицаСвязей = НайтиПоСсылкам(МассивДокументов);

// Создаем результирующую таблицу для хранения найденных документов-потомков
ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("ДокументПотомок");
ТаблицаРезультат.Колонки.Добавить("ИмяРеквизита");

Для Каждого СтрокаСвязи Из ТаблицаСвязей Цикл
    СсылкаНаОбъект = СтрокаСвязи.Ссылка;
    
    // Проверяем, является ли найденный объект документом
    Если Метаданные.Документы.Содержит(СсылкаНаОбъект.Метаданные()) Тогда
        НоваяСтрока = ТаблицаРезультат.Добавить();
        НоваяСтрока.ДокументПотомок = СсылкаНаОбъект;
        НоваяСтрока.ИмяРеквизита = СтрокаСвязи.Данные; // Имя реквизита, где найдена ссылка
    КонецЕсли;
КонецЦикла;

Преимущества метода:

  1. Абсолютная универсальность. Метод находит связи в шапке, табличных частях, движениях регистров и даже в нетиповых свойствах.
  2. Высокая скорость работы на СУБД. Платформа выполняет поиск на уровне базы данных, используя системные индексы ссылочной целостности.
  3. Простота кода. Нам не нужно предварительно анализировать метаданные и писать сложные циклы сборки динамических запросов.

Ограничения метода:

Метод 2. Использование критериев отбора в запросах

Критерии отбора — это специальный объект метаданных в платформе «1С:Предприятие», предназначенный как раз для быстрой связи разнородных документов по общему реквизиту. В большинстве типовых конфигураций (таких как УТ, ERP, КА, БП) уже существует предопределенный критерий отбора, который называется СвязанныеДокументы.

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

Посмотрим на пример эффективного запроса к критерию отбора:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	СвязанныеДокументы.Ссылка КАК Ссылка
    |ИЗ
    |	КритерийОтбора.СвязанныеДокументы(&ДокументОснование) КАК СвязанныеДокументы";

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

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

Метод 3. Динамический анализ метаданных и обход реквизитов

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

Проанализируем ситуацию в современных конфигурациях ERP-класса (ERP, Комплексная автоматизация, Управление торговлей). В этих системах связь документов часто строится по принципу «многие-ко-многим». Это означает, что один документ-потомок может быть введен на основании нескольких документов одновременно. В таких случаях ссылка на основание хранится не в шапке, а в табличной части (например, ТЧ ДокументыОснования или Основания).

Ниже представлен исправленный, надежный и оптимизированный алгоритм динамического поиска реквизитов, который анализирует как шапку, так и все табличные части документов:


Функция ПолучитьКартуСвязейДокументов(ТипОснования) Экспорт
    // Соответствие будет хранить: Ключ - ИмяДокумента, Значение - Массив имен реквизитов (или путей ТЧ.Реквизит)
    КартаСвязей = Новый Соответствие;
    
    Для Каждого МетаДокумент Из Метаданные.Документы Цикл
        НайденныеПути = Новый Массив;
        
        // 1. Поиск в реквизитах шапки
        Для Каждого РеквизитШапки Из МетаДокумент.Реквизиты Цикл
            Если РеквизитШапки.Тип.СодержитТип(ТипОснования) Тогда
                НайденныеПути.Добавить("Шапка." + РеквизитШапки.Имя);
            КонецЕсли;
        КонецЦикла;
        
        // 2. Поиск в реквизитах всех табличных частей
        Для Каждого ТабличнаяЧасть Из МетаДокумент.ТабличныеЧасти Цикл
            Для Каждого РеквизитТЧ Из ТабличнаяЧасть.Реквизиты Цикл
                Если РеквизитТЧ.Тип.СодержитТип(ТипОснования) Тогда
                    НайденныеПути.Добавить("ТЧ." + ТабличнаяЧасть.Имя + "." + РеквизитТЧ.Имя);
                КонецЕсли;
            КонецЦикла;
        КонецЦикла;
        
        // Если нашли связи, фиксируем их для этого документа
        Если НайденныеПути.Количество() > 0 Тогда
            КартаСвязей.Вставить(МетаДокумент.Имя, НайденныеПути);
        КонецЕсли;
    КонецЦикла;
    
    Возврат КартаСвязей;
КонецФункции

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

Метод 4. Программный интерфейс БСП (Библиотека стандартных подсистем)

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

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

Метод 5. Использование регистров сведений для хранения связей

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

При записи или проведении любого подчиненного документа в системе срабатывает подписка на событие, которая записывает пару "Основание — Потомок" в специализированный индексированный регистр сведений (например, РегистрСведений.СвязиДокументов).

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	СвязиДокументов.ДокументПотомок КАК Документ
    |ИЗ
    |	РегистрСведений.СвязиДокументов КАК СвязиДокументов
    |ГДЕ
    |	СвязиДокументов.ДокументОснование = &ДокументОснование";

Запрос.УстановитьПараметр("ДокументОснование", СсылкаНаОснование);
Результат = Запрос.Выполнить().Выгрузить();

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

Резюме: какой метод выбрать в реальном проекте?

Подведем итоги нашего детального разбора и определим оптимальные сценарии применения каждого метода:

  1. Используйте Критерии отбора (Метод 2) или готовые механизмы БСП (Метод 4), если вы работаете в стандартной типовой конфигурации 1С. Это самый простой, быстрый и поддерживаемый путь разработки.
  2. Используйте метод НайтиПоСсылкам() (Метод 1) в привилегированном модуле на сервере, если вам требуется стопроцентная универсальность решения в абсолютно любой, даже незнакомой или самописной конфигурации.
  3. Дорабатывайте и используйте динамический обход метаданных (Метод 3) с кэшированием результатов в модуле повторного использования, если вам необходимо гибко управлять процессом генерации SQL-запросов и избегать избыточных блокировок СУБД.
← На главную