Как программно выбрать все документы, в которых используется элемент справочника в 1С?

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

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

Способ 1. Использование платформенного метода "НайтиПоСсылкам"

В платформе 1С существует встроенный метод НайтиПоСсылкам, который возвращает таблицу значений со всеми найденными ссылками. Рассмотрим подробнее, как правильно его использовать и каких ошибок стоит избегать.

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


Выборка = Справочники.ДоговорыКонтрагентов.Выбрать();
МассивИскомыхСсылок = Новый Массив;

Пока Выборка.Следующий() Цикл
    // Обязательно добавляем ссылку, а не саму выборку
    МассивИскомыхСсылок.Добавить(Выборка.Ссылка); 
КонецЦикла;

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

Метод возвращает таблицу значений с колонками: Ссылка (где нашли), Данные (найденный объект) и Метаданные. Поскольку наша задача — найти только документы, нам необходимо отфильтровать результат, так как метод найдет ссылки и в других справочниках, регистрах и так далее.

Проанализируем ситуацию: как понять, что найденная строка принадлежит документу? Для этого есть два надежных варианта:

  1. Проверка через коллекцию метаданных: Метаданные.Документы.Содержит(СтрокаТЗ.Метаданные)
  2. Проверка через типы ссылок документов: Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(СтрокаТЗ.Ссылка))

Пример обхода результатов:


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

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

Способ 2. Динамическое формирование запроса по метаданным

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

Выясним причину распространенной ошибки: иногда разработчики ищут реквизит по его имени (например, "Организация" или "Контрагент"). Это неверно, так как ссылка может храниться в реквизите с именем "Грузоотправитель" или "Плательщик". Правильный подход — проверять тип реквизита: может ли он хранить искомую ссылку.

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


ИскомаяСсылка = Справочники.ДоговорыКонтрагентов.НайтиПоКоду("00001");
ТипИскомойСсылки = ТипЗнч(ИскомаяСсылка);

ТекстЗапроса = "";
Для Каждого ВидДок Из Метаданные.Документы Цикл
    
    // Проверяем реквизиты шапки документа
    ДокументПодходит = Ложь;
    Для Каждого Реквизит Из ВидДок.Реквизиты Цикл
        Если Реквизит.Тип.СодержитТип(ТипИскомойСсылки) Тогда
            ДокументПодходит = Истина;
            Прервать; // Нашли подходящий реквизит, дальше шапку не проверяем
        КонецЕсли;
    КонецЦикла;
    
    // Если документ подходит, добавляем его в общий запрос
    Если ДокументПодходит Тогда
        Если ТекстЗапроса <> "" Тогда
            ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС;
        КонецЕсли;
        
        ТекстЗапроса = ТекстЗапроса + 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |   Ссылка КАК ДокументСсылка
        |ИЗ
        |   Документ." + ВидДок.Имя + " КАК ТекДокумент
        |ГДЕ
        |   ТекДокумент.ПометкаУдаления = ЛОЖЬ"; 
        // В реальной задаче здесь нужно прописывать условия сравнения по конкретным найденным реквизитам
    КонецЕсли;
КонецЦикла;

Если ТекстЗапроса <> "" Тогда
    Запрос = Новый Запрос(ТекстЗапроса);
    Результат = Запрос.Выполнить().Выгрузить();
КонецЕсли;

Способ 3. Использование механизма "Критерии отбора"

Самый быстрый и архитектурно правильный способ, если поиск по данному справочнику требуется в системе регулярно. В дереве конфигурации 1С есть специальная ветка Критерии отбора. Этот объект метаданных связывает между собой различные документы и справочники по заданному типу данных.

Если для нужного справочника уже настроен критерий отбора (например, "ДокументыПоДоговору"), вам достаточно обратиться к его виртуальной таблице запросом. Это работает мгновенно и не вызывает проблем с производительностью:


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

Способ 4. Использование функций Библиотеки стандартных подсистем (БСП)

В современных конфигурациях 1С (начиная с платформы 8.3) интегрирована БСП. Разработчикам не нужно "изобретать велосипед". В общих модулях есть готовые и протестированные методы. Например, можно использовать функции модуля ОбщегоНазначения.

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

← На главную