Как удалить строки в документе 1С по списку номеров строк

Программист 1С v8.3 (Обычные формы)
← На главную

В процессе разработки в системе «1С:Предприятие» часто возникает задача удаления определенных строк из табличной части документа. Ситуация осложняется, когда критерием удаления является именно номер строки. Главная проблема заключается в том, что после удаления первой же строки индексы всех последующих строк уменьшаются на единицу, и классический перебор «сверху вниз» приводит к пропуску нужных строк или удалению неверных данных. Рассмотрим несколько эффективных способов решения этой задачи, от простых циклов до высокопроизводительных методов работы с памятью.

Проблема смещения индексов при удалении

Разберем ситуацию: у нас есть документ, содержащий 4000 строк в табличной части Материалы. Нам нужно удалить 1000 строк, номера которых хранятся в отдельной таблице или массиве. Если мы начнем удалять строки по порядку (1, 5, 10...), то после удаления строки №1 строка, которая была №2, станет №1. В итоге наш алгоритм, обратившись к следующему индексу, пропустит данные или обратится к некорректной позиции. Проанализируем способы, позволяющие этого избежать.

Способ 1. Удаление в обратном цикле

Это классический и самый надежный метод при работе с индексами коллекций. Суть заключается в том, что мы начинаем перебор табличной части с конца (с самого большого номера к меньшему). В этом случае удаление нижней строки никак не влияет на номера строк, расположенных выше.

Рассмотрим пример кода, где СтрокиВДокументах — это таблица с номерами строк для удаления, а ТН — ссылка на документ:


ДокОбъект = ТН.ПолучитьОбъект();
КоллекцияСтрок = ДокОбъект.Материалы;

// Сортируем таблицу с номерами строк по убыванию, чтобы идти снизу вверх
СтрокиВДокументах.Сортировать("СтрокаТН Убыв");

Для Каждого СтрИзСписка Из СтрокиВДокументах Цикл
    НомерДляПоиска = Число(СтрИзСписка.СтрокаТН);
    
    // Ищем строку по ее номеру. Важно: НомерСтроки — это штатный реквизит ТЧ
    СтрокаТЧ = КоллекцияСтрок.Найти(НомерДляПоиска, "НомерСтроки");
    
    Если СтрокаТЧ <> Неопределено Тогда
        КоллекцияСтрок.Удалить(СтрокаТЧ);
    КонецЕсли;
КонецЦикла;

ДокОбъект.Записать();

Важный нюанс: при использовании метода Удалить(СтрокаОбъект) платформа сама находит нужный индекс. Если же вы используете удаление по индексу Удалить(Индекс), помните, что в 1С НомерСтроки начинается с 1, а индексы в коллекции — с 0.

Способ 2. Использование массива объектов строк

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

Выясним, как это реализовать на практике:


ОбъектДок = ТН.ПолучитьОбъект();
ТабличнаяЧасть = ОбъектДок.Материалы;
МассивСтрокДляУдаления = Новый Массив;

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

// 2. Удаляем собранные строки
Для каждого СтрДляУдаления Из МассивСтрокДляУдаления Цикл
    ТабличнаяЧасть.Удалить(СтрДляУдаления);
КонецЦикла;

ОбъектДок.Записать();

Способ 3. Оптимизированная перезапись через Таблицу Значений

Если в документе очень много строк (десятки тысяч) и нужно удалить значительную их часть, поштучное удаление методом Удалить() может работать медленно. Это связано с тем, что после каждого удаления 1С пересчитывает итоги документа и обновляет индексы коллекции. В таких случаях эффективнее использовать метод «полной замены».

Проанализируем шаги этого алгоритма:

  1. Выгружаем табличную часть в ТаблицаЗначений.
  2. Добавляем в эту таблицу признак удаления или переносим только «хорошие» строки в новую таблицу.
  3. Очищаем табличную часть документа методом Очистить().
  4. Загружаем обратно отфильтрованную таблицу методом Загрузить().

Посмотрим на пример реализации:


ДокОбъект = ТН.ПолучитьОбъект();
ТЗ_Материалы = ДокОбъект.Materials.Выгрузить();

// Добавим колонку для пометки на удаление (для удобства фильтрации)
ТЗ_Материалы.Колонки.Добавить("НужноУдалить", Новый ОписаниеТипов("Булево"));

// Заполняем пометки на основе вашего списка номеров строк
Для каждого Стр Из СтрокиВДокументах Цикл
    Номер = Число(Стр.СтрокаТН);
    // Номер строки в ТЗ соответствует (НомерСтроки - 1)
    Если Номер > 0 И Номер <= ТЗ_Материалы.Количество() Тогда
        ТЗ_Материалы[Номер - 1].НужноУдалить = Истина;
    КонецЕсли;
КонецЦикла;

// Отбираем только те строки, которые НЕ помечены на удаление
ПараметрыОтбора = Новый Структура("НужноУдалить", Ложь);
ТЗ_Остаток = ТЗ_Материалы.Скопировать(ПараметрыОтбора);

// Удаляем служебную колонку перед загрузкой назад
ТЗ_Остаток.Колонки.Удалить("НужноУдалить");

// Обновляем документ
ДокОбъект.Материалы.Загрузить(ТЗ_Остаток);
ДокОбъект.Записать();

Советы по оптимизации

Для обеспечения стабильной работы программы при массовых операциях со строками рекомендуем придерживаться следующих правил:

  1. Работайте на сервере: Все операции по получению объекта документа (ПолучитьОбъект()), перебору строк и записи должны выполняться в серверном контексте. Передача тяжелых объектов между клиентом и сервером существенно замедляет работу.
  2. Используйте транзакции: Если вы обрабатываете несколько документов в цикле, оберните процесс в явную транзакцию (НачатьТранзакцию() ... ЗафиксироватьТранзакцию()). Это обеспечит целостность данных в случае ошибки.
  3. Проверка типов: Часто номера строк в сторонних таблицах приходят в виде строк с неразрывными пробелами. Обязательно используйте Число() и предварительную очистку строки через СтрЗаменить(Текст, Символы.НПП, "").
  4. Обновление формы: Если код выполняется в модуле формы, не забудьте вызвать методы обновления интерфейса после записи объекта, чтобы пользователь сразу увидел результат удаления строк.

Рассмотренные подходы позволяют гибко решать задачу удаления данных. Для небольших документов идеально подходит обратный цикл, а для высоконагруженных систем лучшим выбором будет выгрузка в таблицу значений с последующей загрузкой отфильтрованного результата.

← На главную