В процессе разработки на платформе 1С:Предприятие часто возникает задача анализа ссылочной целостности. Типичный пример — необходимость определить, задействован ли конкретный элемент справочника (например, Контрагенты) в каких-либо документах системы, чтобы принять решение о его удалении или пометке на удаление. Для этого в платформе предусмотрен мощный инструмент — метод НайтиПоСсылкам. Однако при его использовании программисты часто сталкиваются с тем, что фильтрация по типам объектов работает не совсем так, как ожидалось. Разберем подробно, как правильно ограничить область поиска только документами и почему в результатах могут появляться «лишние» данные.
Прежде всего, проанализируем синтаксис функции. Метод НайтиПоСсылкам принимает четыре параметра. Правильное их заполнение — залог корректного результата. Рассмотрим их подробнее:
Неопределено или пропустить параметр.ОбъектМетаданных), по которым нужно производить поиск. Именно сюда мы должны передать список всех документов.Рассмотрим пример кода, который часто используют разработчики, и выясним, где может крыться ошибка. Обратите внимание на количество запятых при вызове функции:
// Правильный вызов с пропуском второго параметра
МассивДокументов = Новый Массив;
Для каждого МетаДок Из Метаданные.Документы Цикл
МассивДокументов.Добавить(МетаДок);
КонецЦикла;
// Вторым параметром идет ОбластьПоиска, которую мы пропускаем (ставим запятую)
ТаблицаСсылок = НайтиПоСсылкам(МассивКонтрагентов, , МассивДокументов);
Если пропустить запятую или перепутать местами параметры ВключитьОбъекты и ИсключитьОбъекты, платформа либо проигнорирует фильтр, либо выдаст ошибку типов.
Даже если мы передали в третий параметр массив только с метаданными документов, в возвращаемой таблице значений могут оказаться записи других типов. Проанализируем причины этой ситуации:
Контрагенты указан в качестве измерения или реквизита в регистре сведений или накопления, и этот регистр имеет регистратор (документ), то при поиске по документам платформа может вернуть записи этих регистров. Технически ссылка находится в таблице регистра, но логически она связана с документом-регистратором.ДоговорыКонтрагентов), ссылки на них могут «выплывать» в общем поиске, если поиск настроен недостаточно строго.Метаданные.Документы может не учесть документы, добавленные в расширениях. Платформа видит эти ссылки, но ваш фильтр их не содержит, что иногда приводит к неоднозначному поведению метода.Чтобы исключить «мусорные» записи из результата, рекомендуется выполнять дополнительную постобработку полученной таблицы значений. Рассмотрим, как это сделать программно:
// Очистка результата от типов, не являющихся документами
Индекс = ТаблицаСсылок.Количество() - 1;
Пока Индекс >= 0 Цикл
Строка = ТаблицаСсылок[Индекс];
Если Не Метаданные.Документы.Содержит(Строка.Метаданные) Тогда
ТаблицаСсылок.Удалить(Строка);
КонецЕсли;
Индекс = Индекс - 1;
КонецЦикла;
Одной из главных ошибок начинающих разработчиков является вызов НайтиПоСсылкам внутри цикла по каждому элементу справочника. Если в справочнике тысячи контрагентов, программа будет работать крайне медленно. Выясним, как ускорить этот процесс.
Метод НайтиПоСсылкам гораздо эффективнее отрабатывает, когда ему передают сразу массив из нескольких сотен или тысяч ссылок. Платформа выполняет один оптимизированный запрос к базе данных вместо множества мелких обращений. Проанализируем правильный алгоритм пакетной обработки:
НайтиПоСсылкам один раз для всего массива.Ссылка (это та ссылка, которую мы искали).Ссылка таблицы результатов, гарантированно не имеют вхождений в документах.Посмотрим на пример реализации такой проверки:
// Собираем все ссылки для проверки
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты ГДЕ НЕ ПометкаУдаления");
МассивВсехКонтрагентов = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
// Ищем ссылки пакетом по всем документам
ТаблицаРезультат = НайтиПоСсылкам(МассивВсехКонтрагентов, , МассивДокументов);
// Теперь анализируем: на кого ссылок НЕТ
Для каждого СсылкаК Из МассивВсехКонтрагентов Цикл
Если ТаблицаРезультат.Найти(СсылкаК, "Ссылка") = Неопределено Тогда
// Этот контрагент чист, его можно помечать на удаление
Объект = СсылкаК.ПолучитьОбъект();
Объект.ПометкаУдаления = Истина;
Объект.Записать();
КонецЕсли;
КонецЦикла;
В некоторых случаях стандартный метод НайтиПоСсылкам может быть избыточным. Если вам нужно проверить наличие контрагента в конкретных, заранее известных таблицах (например, только в РеализацияТоваровУслуг и ПоступлениеТоваровУслуг), использование обычного запроса (Query) будет в десятки и сотни раз быстрее.
Универсальный метод сканирует все таблицы, включая табличные части и реквизиты всех документов, что создает колоссальную нагрузку на дисковую подсистему сервера БД. Если бизнес-логика позволяет ограничить список проверяемых документов пятью-десятью видами, лучше написать прямой запрос к их таблицам.
Также стоит помнить о методе НайтиУдаляемыеОбъекты. Он работает медленнее, так как выполняет более глубокую проверку на возможность удаления объекта, учитывая не только ссылки, но и правила ссылочной целостности, установленные в конфигураторе. Для простых задач «проверки на вхождение» НайтиПоСсылкам остается приоритетным выбором.
Важный момент: если вы работаете в крупной информационной базе (например, 1С:ERP или 1С:УПП), помните о «Legacy-особенностях». В старых релизах платформы при передаче слишком большого массива в параметр ВключитьОбъекты фильтрация могла игнорироваться. В таких случаях стоит передавать не «все документы вообще», а только те, которые действительно могут содержать ссылку на контрагента (отсеивая кадровые документы или внутренние складские перемещения).
Таким образом, для корректного отбора только по документам необходимо:
НайтиПоСсылкам.