При разработке и сопровождении конфигураций 1С часто возникает задача: быстро выяснить, используется ли конкретный элемент (например, контрагент, номенклатура или договор) в документах и регистрах информационной базы. Нередко разработчики пытаются написать универсальный запрос, который сразу выдаст все найденные ссылки. Разберем особенности платформы 1С, ограничения языка запросов и эффективные способы решения этой задачи.
В языке запросов 1С не существует универсальной виртуальной таблицы ссылок или специального оператора, способного одной командой обойти всю базу данных. На уровне СУБД платформа 1С не использует стандартные внешние ключи (Foreign Keys) для обеспечения ссылочной целостности — этот контроль полностью возложен на механизмы самой платформы. Запрос всегда должен обращаться к строго определенным таблицам метаданных, указанным в секции ИЗ.
Если нам необходимо проверить наличие ссылок именно через язык запросов, у нас есть несколько вариантов реализации в зависимости от архитектуры конфигурации.
Если круг документов, где может использоваться объект, заранее известен и ограничен, можно составить прямой запрос. Ссылки могут находиться как в реквизитах шапки, так и в табличных частях. Рассмотрим пример запроса, объединяющего проверку шапки и табличной части документа:
ВЫБРАТЬ ПЕРВЫЕ 1
Док.Ссылка КАК Ссылка
ИЗ
Документ.РеализацияТоваровУслуг КАК Док
ГДЕ
Док.Контрагент = &Контрагент
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ПЕРВЫЕ 1
ТЧ.Ссылка КАК Ссылка
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
ГДЕ
ТЧ.Контрагент = &Контрагент
Обратите внимание на конструкцию ВЫБРАТЬ ПЕРВЫЕ 1. Если цель проверки — лишь подтвердить факт использования объекта, нет необходимости считывать тысячи записей. Достаточно получить первую найденную запись, что существенно снижает нагрузку на СУБД.
Если в конфигурации настроен журнал документов, включающий в себя все интересующие виды документов, и в нем выведена соответствующая графа (например, Контрагент), задачу можно решить одним простым запросом к журналу:
ВЫБРАТЬ ПЕРВЫЕ 1
Журнал.Ссылка КАК ДокументСсылка
ИЗ
ЖурналДокументов.ДокументыКонтрагентов КАК Журнал
ГДЕ
Журнал.Контрагент = &Контрагент
Этот способ работает быстро, так как обращение идет к единой таблице журнала. Однако он применим только тогда, когда графа журнала охватывает все нужные типы документов и ссылки не спрятаны в табличных частях, не вошедших в журнал.
В типовых конфигурациях для поиска связанных документов и справочников часто применяются критерии отбора (например, КритерииОтбора.СвязанныеДокументы). Если целевой объект включен в состав критерия отбора, к нему можно обратиться через запрос:
ВЫБРАТЬ ПЕРВЫЕ 1
Критерий.Ссылка КАК Ссылка
ИЗ
КритерийОтбора.СвязанныеДокументы(&Контрагент) КАК Критерий
Критерий отбора формирует обращение ко всем объектам, входящим в его состав, избавляя программиста от необходимости вручную перечислять каждый документ.
Когда требуется выполнить полный поиск ссылок по всей конфигурации, стандартным и наиболее надежным инструментом платформы является метод НайтиПоСсылкам(). Он обходит все структуры данных, где может встретиться ссылка.
Разберем базовый пример использования:
МассивСсылок = Новый Массив;
МассивСсылок.Добавить(НужныйКонтрагент);
НайденныеСсылки = НайтиПоСсылкам(МассивСсылок);
Если НайденныеСсылки.Количество() > 0 Тогда
// Объект используется в базе
КонецЕсли;
У метода НайтиПоСсылкам() есть важные параметры, о которых часто забывают: ОбластьПоиска и СписокНепроверяемыхДанных. По умолчанию метод сканирует абсолютно все таблицы базы данных, что на больших объемах приводит к серьезным задержкам. Чтобы ускорить работу, ограничим область поиска только нужными объектами метаданных:
МассивСсылок = Новый Массив;
МассивСсылок.Добавить(НужныйКонтрагент);
ОбластьПоиска = Новый Массив;
ОбластьПоиска.Добавить(Метаданные.Документы.РеализацияТоваровУслуг);
ОбластьПоиска.Добавить(Метаданные.Документы.ПоступлениеТоваровУслуг);
НайденныеСсылки = НайтиПоСсылкам(МассивСсылок, ОбластьПоиска);
Если задача заключается исключительно в быстрой проверке факта наличия ссылок без получения полного списка вхождений, штатный НайтиПоСсылкам() может оказаться избыточным, так как он ищет абсолютно все записи. В таких случаях строят динамический пакетный запрос с помощью обхода метаданных:
Метаданные.Документы (а также справочники и регистры при необходимости).Метаданные.СвойстваОбъектов.Тип.СодержитТип(...).ВЫБРАТЬ ПЕРВЫЕ 1.ОБЪЕДИНИТЬ ВСЕ и выполняем запрос.СУБД остановит выполнение запроса на первом же найденном совпадении, что даст колоссальный выигрыш в скорости по сравнению с полным перебором.
Ограничиваясь только проверкой документов, легко пропустить другие важные объекты. Ссылки на справочники могут храниться в следующих структурах базы данных:
При поиске ссылок на больших объемах данных необходимо учитывать влияние блокировок. Массовое чтение таблиц накладывает разделяемые блокировки (Shared Locks) на считываемые диапазоны данных в СУБД. Выполнение масштабного поиска ссылок внутри транзакции записи категорически не рекомендуется, так как это провоцирует взаимоблокировки (deadlocks) и замедляет работу пользователей. Подобные проверки следует выносить за рамки транзакций либо передавать на исполнение в фоновые задания.