Как найти строки в таблице значений с условием «Не равно»?

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия IT и автоматизация бизнеса
← На главную

В процессе разработки часто возникает задача отобрать строки в объекте ТаблицаЗначений сразу по нескольким критериям. Стандартный метод НайтиСтроки() принимает на вход объект Структура, где ключами выступают имена колонок, а значениями — параметры поиска. Однако структура отбора поддерживает проверку только на строгое равенство. Если требуется отобрать строки, совпадающие по нескольким полям (например, серия и номер бланка), но исключить текущий документ по ссылке (условие НеРавно), стандартная передача параметров в структуру не сработает.

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

Способ 1. Отбор по равенству с последующей фильтрацией массива (рекомендуемый)

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

Разберем по шагам, как построить эффективный алгоритм с помощью предварительной фильтрации и постобработки:

  1. Индексация колонок: если таблица содержит сотни или тысячи строк, а поиск выполняется регулярно, добавим индекс по колонкам строгого равенства. Это переведет поиск в сложность O(1).
  2. Поиск по равенству: отберем строки через НайтиСтроки() по условиям равенства (в нашем примере — серия и номер).
  3. Исключение лишних строк в цикле: обойдем полученный небольшой массив с конца и удалим элементы, которые не удовлетворяют условию исключения.

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


// Добавляем индекс для ускорения поиска (выполняется один раз при заполнении ТЗ)
ТЗ.Индексы.Добавить("СерияБСО, НомерБСО");

// 1. Формируем структуру только для условий равенства
СтруктураОтбора = Новый Структура;
СтруктураОтбора.Вставить("СерияБСО", СерияБСО);
СтруктураОтбора.Вставить("НомерБСО", НомерБСО);

НайденныеСтроки = ТЗ.НайтиСтроки(СтруктураОтбора);

// 2. Исключаем строки с текущей ссылкой, обходя массив с конца
Для Счетчик = НайденныеСтроки.ВГраница() По 0 Шаг -1 Цикл
    Если НайденныеСтроки[Счетчик].Ссылка = ТекСсылка Тогда
        НайденныеСтроки.Удалить(Счетчик);
    КонецЕсли;
КонецЦикла;

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

Способ 2. Фильтрация с помощью объекта ПостроительЗапроса

Рассмотрим альтернативный вариант, когда требуется получить результат в виде готовой отфильтрованной таблицы значений без ручного обхода элементов. Для этого задействуем ПостроительЗапроса, передав в него таблицу через ОписаниеИсточникаДанных. Коллекция Отбор построителя позволяет гибко задавать различные виды сравнений, включая ВидСравнения.НеРавно.

Посмотрим на пример кода с использованием построителя запроса:


СтруктураОтбора = Новый Структура;
СтруктураОтбора.Вставить("СерияБСО", СерияБСО);
СтруктураОтбора.Вставить("НомерБСО", НомерБСО);
СтруктураОтбора.Вставить("Ссылка", ТекСсылка);

СтруктураСравнений = Новый Структура;
СтруктураСравнений.Вставить("СерияБСО", ВидСравнения.Равно);
СтруктураСравнений.Вставить("НомерБСО", ВидСравнения.Равно);
СтруктураСравнений.Вставить("Ссылка", ВидСравнения.НеРавно);

ПостроительЗапроса = Новый ПостроительЗапроса;
ПостроительЗапроса.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТЗ);

Для Каждого Критерий Из СтруктураОтбора Цикл
    НовыйОтбор = ПостроительЗапроса.Отбор.Добавить(Критерий.Ключ);
    НовыйОтбор.Использование = Истина;
    НовыйОтбор.ВидСравнения = СтруктураСравнений[Критерий.Ключ];
    НовыйОтбор.Значение = Критерий.Значение;
КонецЦикла;

НоваяТЗ = ПостроительЗапроса.Результат.Выгрузить();

Разберем подробнее важные нюансы этого подхода:

  1. Потеря ссылочной целостности: метод Результат.Выгрузить() создает абсолютно новую таблицу значений (копию данных). Изменение колонок в НоваяТЗ не изменит данные в исходной таблице ТЗ.
  2. Отсутствие индексации: ПостроительЗапроса при фильтрации источника данных выполняет последовательный перебор всех строк в оперативной памяти и не задействует коллекцию Индексы таблицы значений.
  3. Работа с типами: в отличие от прямого использования объекта Запрос с передачей таблицы значений во временную таблицу, ПостроительЗапроса через ОписаниеИсточникаДанных обрабатывает данные в памяти и не требует строгой типизации колонок.

Способ 3. Прямой обход таблицы циклом

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

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


НайденныеСтроки = Новый Массив;

Для Каждого Стр Из ТЗ Цикл
    Если Стр.СерияБСО = СерияБСО 
       И Стр.НомерБСО = НомерБСО 
       И Стр.Ссылка <> ТекСсылка Тогда
        НайденныеСтроки.Добавить(Стр);
    КонецЕсли;
КонецЦикла;

Этот способ максимально нагляден, сохраняет прямые ссылки на строки исходной таблицы и работает с любыми произвольными типами данных в колонках.

Сравнение подходов и выводы

Проанализируем, в каких ситуациях какой метод предпочтителен:

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