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

База знаний Одина — Одинэсника Форумы ODIN — Форум по 1С Предприятию Как оптимизировать удаление объектов в 1С без повторных вызовов поиска ссылок?

Просмотр 1 ветки ответов
  • Автор
    Сообщения
    • #37486
      Фото аватараOdineski
      Участник

        1с 8.3 клиент-сервер обычные формы.
        Требуется оптимизировать процедуру поиска и удаления помеченных на удаление объектов.
        В функции МожноУдалитьСправочникСКаскадом для каждого элемента производится поиск ссылок методом НайтиПоСсылкам. Это очень долго. А эти ссылки уже есть в массиве МассивПомеченных (процедура УдалитьПомеченныеОбъекты)
        // Главная процедура для регламентного задания
        Процедура УдалитьПомеченныеОбъекты() Экспорт

        ИмяСобытияЖурнала = «РегламентноеЗадание.УдалениеОбъектов»;

        Попытка
        МассивПомеченных = НайтиПомеченныеНаУдаление();
        Если МассивПомеченных.Количество() = 0 Тогда
        Возврат;
        КонецЕсли;

        МассивДляУдаления = Новый Массив;

        Для Каждого Ссылка Из МассивПомеченных Цикл

        // Проверяем, является ли объект элементом справочника
        Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Ссылка)) Тогда

        // Выполняем каскадный анализ и пометку подчиненных структур
        Если МожноУдалитьСправочникСКаскадом(Ссылка, ИмяСобытияЖурнала) Тогда
        МассивДляУдаления.Добавить(Ссылка);
        КонецЕсли;

        Иначе
        // Для остальных типов (документы и т.д.) оставляем стандартное поведение
        МассивДляУдаления.Добавить(Ссылка);
        EndIf;

        КонецЦикла;

        // Финальное удаление объектов, прошедших проверку (с контролем оставшихся ссылок)
        Если МассивДляУдаления.Количество() > 0 Тогда
        МассивНеудаленных = Новый Массив;
        УдалитьОбъекты(МассивДляУдаления, Истина, МассивНеудаленных);
        КонецЕсли;

        Исключение
        ЗаписьЖурналаРегистрации(ИмяСобытияЖурнала, УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
        КонецПопытки;

        КонецПроцедуры

        // Функция проверяет ссылки и каскадно помечает разрешенные зависимости
        Функция МожноУдалитьСправочникСКаскадом(ГлавнаяСсылка, ИмяСобытияЖурнала)

        МассивДляПоиска = Новый Массив;
        МассивДляПоиска.Добавить(ГлавнаяСсылка);

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

        МассивПодчиненныхДляПометки = Новый Массив;
        НаборыЗаписейРегистров = Новый Массив;

        Для Каждого СтрокаСсылок Из ТаблицаСсылок Цикл

        СсылкаНаЗависимость = СтрокаСсылок.Ссылка;
        МетаданныеЗависимости = СсылкаНаЗависимость.Метаданные();

        // 1. Проверяем, является ли это подчиненным справочником
        Если Метаданные.Справочники.Содержит(МетаданныеЗависимости) Тогда

        // Проверяем, является ли главная ссылка владельцем или родителем
        ЭтоПодчиненный = (СсылкаНаЗависимость.Владелец = ГлавнаяСсылка);

        Если ЭтоПодчиненный Тогда
        МассивПодчиненныхДляПометки.Добавить(СсылкаНаЗависимость);
        Иначе
        // Ссылка найдена в обычном реквизите другого справочника — удалять нельзя
        Возврат Ложь;
        КонецЕсли;

        // 2. Проверяем, является ли это Регистром Сведений
        ИначеЕсли Метаданные.РегистрыСведений.Содержит(МетаданныеЗависимости) Тогда

        // Нам подходят только регистры, где объект — Измерение.
        // Проверяем имя метаданного через его тип (для РС ссылка возвращает КлючЗаписи)
        ИмяРегистра = МетаданныеЗависимости.Имя;
        ИмяРеквизитаИлиИзмерения = СтрокаСсылок.Метаданные.Имя;

        // Проверяем, что ссылка находится именно в коллекции Измерения
        Если МетаданныеЗависимости.Измерения.Найти(ИмяРеквизитаИлиИзмерения) = Неопределено Тогда
        // Ссылка найдена в Ресурсе или Реквизите регистра — удалять нельзя
        Возврат Ложь;
        КонецЕсли;

        // Создаем объект набора записей для последующего удаления очисткой
        Набор = РегистрыСведений[ИмяРегистра].СоздатьНаборЗаписей();
        Набор.Отбор[ИмяРеквизитаИлиИзмерения].Установить(ГлавнаяСсылка);
        НаборыЗаписейРегистров.Добавить(Набор);

        Иначе
        // Ссылка найдена в Документе, Регистре Накопления и т.д. — удалять нельзя
        Возврат Ложь;
        КонецЕсли;

        КонецЦикла;

        // Если дошли сюда — объект окружен только разрешенными зависимостями.
        // Начинаем транзакцию очистки и пометки.
        НачатьТранзакцию();
        Попытка

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

        // Очищаем записи регистров сведений, где объект был измерением
        Для Каждого НаборЗаписей Из НаборыЗаписейРегистров Цикл
        НаборЗаписей.Записать(Истина);
        КонецЦикла;

        ЗафиксироватьТранзакцию();
        Возврат Истина;

        Исключение
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации(ИмяСобытияЖурнала, УровеньЖурналаРегистрации.Предупреждение, , ,
        «Ошибка каскадной пометки для » + ГлавнаяСсылка + «: » + ОписаниеОшибки());
        Возврат Ложь;
        КонецПопытки;

        КонецФункции

      • #37487
        bot_avaOdin — Одинэсник
        Участник

          Чтобы оптимизировать процедуру удаления помеченных объектов и избежать повторных вызовов НайтиПоСсылкам для каждого элемента, нужно использовать уже имеющийся массив помеченных объектов (МассивПомеченных) как источник для поиска ссылок, а не делать отдельный поиск для каждого объекта.

          Основная идея — один раз построить структуру, которая позволит быстро проверять, есть ли ссылки на каждый из объектов из МассивПомеченных, и использовать эту структуру в функции МожноУдалитьСправочникСКаскадом.

          Вот конкретный план и пример реализации:

          1. Собрать все ссылки на объекты из МассивПомеченных одним вызовом НайтиПоСсылкам — это уже есть в процедуре УдалитьПомеченныеОбъекты.

          2. Построить индекс ссылок по объектам, на которые ссылаются — например, создать соответствие, где ключ — ссылка на объект из МассивПомеченных, а значение — массив ссылок, которые на него ссылаются.

          3. Передавать этот индекс в функцию МожноУдалитьСправочникСКаскадом, чтобы она не вызывала НайтиПоСсылкам повторно, а брала ссылки из подготовленного индекса.

          4. В функции МожноУдалитьСправочникСКаскадом использовать переданный индекс для проверки ссылок.

          ### Пример кода с оптимизацией

          ### Ключевые моменты:

          НайтиПоСсылкам вызывается один раз для всего массива помеченных объектов.
          — Построен индекс ссылок для быстрого доступа к ссылкам на конкретный объект.
          — Функция МожноУдалитьСправочникСКаскадом принимает этот индекс и не делает повторных запросов.
          — Если индекс не передан, функция работает как раньше (для обратной совместимости).

          Такой подход значительно уменьшит количество вызовов поиска ссылок и ускорит процедуру удаления, особенно при большом количестве помеченных объектов.

      Просмотр 1 ветки ответов
      • Для ответа в этой теме необходимо авторизоваться.
      База знаний 1С