Можно ли в таблице значений найти строки с отбором колонки по списку значений?

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

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

Почему НайтиСтроки() не работает со списком значений напрямую?

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

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

Рассмотрим пример ошибочного кода:


// Это не будет работать для поиска элементов списка
СписокНужныхТоваров = Новый СписокЗначений;
СписокНужныхТоваров.Добавить(Ссылка1);
СписокНужныхТоваров.Добавить(Ссылка2);

Отбор = Новый Структура("Номенклатура", СписокНужныхТоваров);
// Метод ищет ячейку, где хранится сам объект "СписокЗначений", а не Ссылка1 или Ссылка2
НайденныеСтроки = ТЗ.НайтиСтроки(Отбор); 

Способ №1: Использование Построителя отчета (ОтобратьСтрокиПоКритериям)

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

Разберем по шагам, как реализовать такой поиск самостоятельно:

  1. Создадим объект ПостроительОтчета.
  2. Назначим нашу таблицу значений в качестве источника данных. Для этого используется объект ОписаниеИсточникаДанных.
  3. Настроим программный отбор, указав вид сравнения ВСписке.
  4. Выполним построение и получим результат.

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


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

// Добавляем отбор по нужной колонке
НовыйОтбор = Построитель.Отбор.Добавить("Номенклатура");
НовыйОтбор.Использование = Истина;
НовыйОтбор.ВидСравнения = ВидСравнения.ВСписке;
НовыйОтбор.Значение = СписокНоменклатуры; // Наш СписокЗначений

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

Этот способ официально "не является запросом" в контексте написания текста ВЫБРАТЬ... ИЗ..., хотя технически платформа использует похожие механизмы обработки данных в памяти.

Способ №2: Метод «Вспомогательной колонки» (Маркировка)

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

Проанализируем ситуацию: нам нужно быстро "отловить" все подходящие строки за один или два прохода. Алгоритм действий следующий:

  1. Добавляем в исходную таблицу ТЗ временную колонку типа Булево, например, с именем ЭтоНужнаяСтрока.
  2. Проходим циклом по таблице и проверяем вхождение значения в список.
  3. Используем НайтиСтроки() по нашей булевой колонке.

Пример кода:


ТЗ.Колонки.Добавить("ЭтоНужнаяСтрока", Новый ОписаниеТипов("Булево"));

// Помечаем строки
Для Каждого СтрокаТЗ Из ТЗ Цикл
    Если СписокНоменклатуры.НайтиПоЗначению(СтрокаТЗ.Номенклатура) <> Неопределено Тогда
        СтрокаТЗ.ЭтоНужнаяСтрока = Истина;
    КонецЕсли;
КонецЦикла;

// Теперь НайтиСтроки отработает мгновенно по точному соответствию
НужныеСтроки = ТЗ.НайтиСтроки(Новый Структура("ЭтоНужнаяСтрока", Истина));

// Не забываем удалить временную колонку, если она больше не нужна
ТЗ.Колонки.Удалить("ЭтоНужнаяСтрока");

Способ №3: Поиск перебором списка (для маленьких списков)

Если ваш СписокЗначений содержит всего 2-5 элементов, а сама ТаблицаЗначений огромна, иногда эффективнее запустить цикл по самому списку и накапливать результаты поиска в общем массиве. В этом случае мы будем использовать преимущество индексации таблицы значений (если индекс предварительно создан).

Рассмотрим, как это сделать правильно:


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

ОбщийМассивСтрок = Новый Массив;

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

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

Выводы и рекомендации

Подведем итоги нашего анализа. Прямого способа передать список в НайтиСтроки() не существует из-за архитектурных ограничений метода. Выбор альтернативного решения зависит от ваших задач:

Помните, что производительность — это ключевой фактор. Индексируйте колонки перед поиском и выбирайте тот инструмент, который лучше всего подходит под конкретный объем данных.

← На главную