При работе с крупными базами данных 1С, где счет элементов в справочниках (например, ДоговорыКонтрагентов) идет на десятки и сотни тысяч, стандартный метод НайтиПоСсылкам() начинает работать крайне медленно. Это происходит из-за того, что платформа последовательно перебирает все таблицы метаданных, в которых теоретически может встретиться ссылка на данный объект. В этой статье мы разберем, как оптимизировать этот процесс, внедрим многопоточность и рассмотрим альтернативные методы поиска «мусорных» объектов.
Стандартный метод НайтиПоСсылкам() выполняет поиск по всем объектам метаданных, где тип реквизита совпадает с типом искомого объекта. Если в конфигурации сотни документов и регистров, поиск для каждого отдельного элемента превращается в тяжелую операцию. Ситуация усугубляется, если база работает на PostgreSQL, так как планы запросов при массовых объединениях (UNION ALL) могут быть неоптимальными. Выясним, как обойти эти ограничения.
Одним из самых быстрых способов проверки наличия ссылок является использование объекта конфигурации КритерииОтбора. Если в конфигурации уже есть критерий, например, ДокументыПоДоговоруКонтрагента, обращение к нему происходит значительно быстрее, чем общий поиск ссылок. Это связано с тем, что критерий отбора — это заранее описанный состав таблиц, по которым СУБД может эффективно искать данные.
Рассмотрим пример запроса, который позволяет мгновенно проверить, используется ли договор в основных документах:
ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 1
Критерий.Ссылка КАК Ссылка
ИЗ
КритерийОтбора.ДокументыПоДоговоруКонтрагента(&ДоговорНаПроверку) КАК Критерий
Преимущество: такой запрос выполняется доли секунды даже в базах объемом в несколько сотен гигабайт. Если критерий возвращает хотя бы одну запись, договор точно используется, и его можно исключить из дальнейшей обработки.
Вместо того чтобы проверять каждый договор по отдельности, проанализируем ситуацию глобально. Мы можем составить список всех используемых договоров и вычесть их из общего списка справочника. Это наиболее эффективный подход для массовой очистки.
Разберем алгоритм по шагам:
РегистрБухгалтерии, РегистрНакопления.Взаиморасчеты, основные документы).NULL.Пример реализации такого запроса:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Таблица.Договор КАК Договор
ПОМЕСТИТЬ ВТ_ИспользуемыеДоговоры
ИЗ
(ВЫБРАТЬ ДоговорКонтрагента ИЗ Документ.РеализацияТоваровУслуг
ОБЪЕДИНИТЬ
ВЫБРАТЬ ДоговорКонтрагента ИЗ Документ.ПоступлениеТоваровУслуг
ОБЪЕДИНИТЬ
ВЫБРАТЬ Субконто1 ИЗ РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто
ГДЕ Субконто1 ССЫЛКА Справочник.ДоговорыКонтрагентов) КАК Таблица;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Договоры.Ссылка
ИЗ
Справочник.ДоговорыКонтрагентов КАК Договоры
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИспользуемыеДоговоры КАК Используемые
ПО Договоры.Ссылка = Используемые.Договор
ГДЕ
Используемые.Договор ЕСТЬ NULL
И НЕ Договоры.ПометкаУдаления
Такой подход позволяет за один проход отсеять 95-99% используемых объектов. Оставшиеся «подозрительные» элементы можно допроверить штатным методом НайтиПоСсылкам().
Если справочник огромен, и нам все же нужно использовать НайтиПоСсылкам() для финальной проверки, необходимо распределить нагрузку между несколькими фоновыми заданиями. Проанализируем, как правильно разделить данные.
Разделение по ГУИД (GUID): Самый надежный способ разделить справочник на равные порции без изменения его структуры — использовать последний символ уникального идентификатора. ГУИДы распределены равномерно, что дает предсказуемую нагрузку на каждый поток.
Рассмотрим пример кода для запуска потоков:
КоличествоПотоков = 8;
Для НомерПотока = 1 По КоличествоПотоков Цикл
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(НомерПотока);
ПараметрыЗадания.Добавить(КоличествоПотоков);
ФоновыеЗадания.Выполнить("МойМодуль.ПроверитьПорциюДоговоров", ПараметрыЗадания);
КонецЦикла;
Внутри процедуры ПроверитьПорциюДоговоров мы будем использовать запрос с условием на ГУИД. В 1С нельзя напрямую получить последний символ ГУИДа в запросе, поэтому можно предварительно подготовить массив ссылок или использовать вспомогательный реквизит.
Другой вариант — использовать Планы обмена. Мы регистрируем все элементы справочника на фиктивном узле плана обмена, а каждый поток считывает свою порцию регистраций, обрабатывает их и удаляет.
Часто объекты нельзя удалить, потому что на них ссылаются «бесполезные» записи: технические регистры сведений (например, версионирование объектов), непроведенные документы или служебные настройки. Рассмотрим, как игнорировать такие ссылки.
Метод НайтиПоСсылкам() принимает вторым параметром массив объектов метаданных, в которых нужно искать. Мы можем заранее составить «белый список» важных таблиц (Документы, Регистры бухгалтерии, Регистры накопления) и искать только в них.
ОбъектыДляПоиска = Новый Массив;
ОбъектыДляПоиска.Добавить(Метаданные.Документы.РеализацияТоваровУслуг);
ОбъектыДляПоиска.Добавить(Метаданные.РегистрыНакопления.ВзаиморасчетыСКонтрагентами);
// Добавляем только значимые метаданные
ТаблицаСсылок = НайтиПоСсылкам(МассивПроверяемыхОбъектов, ОбъектыДляПоиска);
Важный нюанс: если вы нашли ссылки только в «мусорных» регистрах, вы можете программно очистить эти записи, после чего объект станет доступен для удаления.
Поскольку ваша база работает на PostgreSQL, следует учитывать, что прямые SQL-запросы к таблицам 1С требуют понимания структуры хранения UUID. 1С хранит ссылки в формате bytea длиной 16 байт. Если вы решите писать прямой SQL-скрипт для поиска ссылок, помните, что идентификаторы в базе перевернуты (хранятся в Big-Endian или смешанном формате).
Проанализируем пример логики SQL для поиска ссылок в конкретной таблице:
SELECT _IDRRef FROM _ReferenceX WHERE _FldY_RRRef = '\x...'::bytea
Однако использование прямого SQL в 1С оправдано только в крайних случаях, так как структура таблиц может меняться при реструктуризации. Лучше использовать механизм External Data Sources (Внешние источники данных) или Query в 1С с предварительным отсевом по метаданным.
Для максимально быстрого поиска неиспользуемых договоров рекомендуем следующий алгоритм:
КритериямОтбора мгновенно исключите договоры, по которым есть документы.НайтиПоСсылкам(), ограничив область поиска только важными метаданными.Такой комбинированный подход позволяет сократить время очистки справочника с нескольких суток до пары часов даже на очень нагруженных системах.