Как удалить лишние строки из табличной части, не соответствующие результатам запроса?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Метод 1: Прямое удаление в цикле (классический подход)

Самый очевидный путь — это перебор всех строк текущей табличной части и проверка каждой строки на наличие в результатах запроса. Однако здесь новичков часто подстерегает ловушка. Если мы будем удалять строки в обычном цикле Для Счетчик = 0 По ТЧ.Количество() - 1, то при удалении строки с индексом 2, бывшая строка 3 станет строкой 2. Счетчик цикла перейдет к значению 3, и мы фактически пропустим одну строку.

Рассмотрим правило «золотого сечения» программиста 1С: удаление строк из коллекции всегда должно производиться с конца в начало. Проанализируем пример кода:


// Предположим, РезультатЗапроса содержит массив или таблицу с Номенклатурой, которую нужно оставить
МассивНужнойНоменклатуры = РезультатЗапроса.ВыгрузитьКолонку("Номенклатура");

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

Разберем, что здесь происходит: мы начинаем с последнего индекса и идем к нулю. При таком подходе удаление текущего элемента никак не влияет на индексы тех элементов, которые нам еще только предстоит обработать. Это гарантирует, что ни одна строка не будет пропущена.

Метод 2: Использование запроса для поиска «лишних» строк

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

Для этого мы можем использовать временные таблицы. Посмотрим на алгоритм действий:

  1. Выгружаем текущую табличную часть в таблицу значений (ТЗ).
  2. Передаем эту ТЗ в запрос в качестве параметра.
  3. В самом запросе выполняем ЛЕВОЕ СОЕДИНЕНИЕ нашей ТЧ с результатами фильтрующего запроса.
  4. Условие ГДЕ Фильтр.Номенклатура ЕСТЬ NULL покажет нам именно те строки, которых нет в «эталонном» списке.

Проанализируем пример такого запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВременнаяТЧ.НомерСтроки КАК НомерСтроки
    |ПОМЕСТИТЬ ВТ_ДанныеТЧ
    |ИЗ
    |   &ТЗ_ТЧ КАК ВременнаяТЧ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   ВТ_ДанныеТЧ.НомерСтроки
    |ИЗ
    |   ВТ_ДанныеТЧ КАК ВТ_ДанныеТЧ
    |       ЛЕВОЕ СОЕДИНЕНИЕ (&РезультатФильтра) КАК ВТ_Фильтр
    |       ПО ВТ_ДанныеТЧ.Номенклатура = ВТ_Фильтр.Номенклатура
    |ГДЕ
    |   ВТ_Фильтр.Номенклатура ЕСТЬ NULL";

Запрос.УстановитьПараметр("ТЗ_ТЧ", Объект.Товары.Выгрузить());
Запрос.УстановитьПараметр("РезультатФильтра", СформированныйРанееРезультат);

Выборка = Запрос.Выполнить().Выбрать();
// Далее удаляем строки, используя полученные номера (также с конца!)

Метод 3: Оптимизация через «Выгрузить» и «Загрузить»

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

Разберем по шагам этот метод:

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

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

Метод 4: Сравнение по составным ключам

Иногда недостаточно сравнивать только по Номенклатура. Бывает, что в ТЧ важна комбинация полей, например, Номенклатура + Характеристика + Склад. В этом случае задача усложняется. Чтобы не запутаться, мы рекомендуем использовать метод соответствия (объект Соответствие) для кэширования эталонных данных.

Посмотрим на пример кэширования для ускорения поиска:


КэшЭталонов = Новый Соответствие;
Для Каждого СтрокаЭталон Из РезультатЗапросаФильтра Цикл
    // Создаем уникальный ключ из нескольких полей
    Ключ = СтрокаЭталон.Номенклатура.УникальныйИдентификатор() + "_" + СтрокаЭталон.Характеристика.УникальныйИдентификатор();
    КэшЭталонов.Вставить(Ключ, Истина);
КонецЦикла;

// Теперь при обходе ТЧ проверка будет мгновенной
Для Счетчик = Объект.Товары.Количество() - 1 По 0 Цикл
    ТекущаяСтрока = Объект.Товары[Счетчик];
    КлючТЧ = ТекущаяСтрока.Номенклатура.УникальныйИдентификатор() + "_" + ТекущаяСтрока.Характеристика.УникальныйИдентификатор();
    
    Если КэшЭталонов[КлючТЧ] = Неопределено Тогда
        Объект.Товары.Удалить(Счетчик);
    КонецЕсли;
КонецЦикла;

Важные технические нюансы

Проанализируем еще несколько моментов, которые помогут избежать ошибок:

Выясним, какой из методов лучше. Для небольших таблиц (до 100 строк) вполне подойдет Метод 1 с обратным циклом. Для сложных условий и средних таблиц удобнее Метод 2 с запросом. Для высоконагруженных систем и огромных ТЧ идеальным выбором станет Метод 3 (Выгрузить/Загрузить), так как он минимизирует количество транзакционных действий с коллекцией.

← На главную