При работе с коллекциями в «1С:Предприятие» часто возникает задача фильтрации данных. Обычный поиск по одному значению через НайтиСтроки() не вызывает вопросов, однако, когда требуется найти строки, где значение колонки входит в определенный СписокЗначений (аналог оператора «В» в языке запросов), разработчики сталкиваются с определенными трудностями. Давайте подробно разберем, почему стандартный метод ведет себя именно так, и какие существуют эффективные обходные пути, если использование полноценного запроса (Запрос) по каким-то причинам нежелательно.
Проанализируем механику работы платформенного метода НайтиСтроки(). Этот метод принимает в качестве параметра структуру, где ключи — это имена колонок, а значения — это то, что мы ищем. Важно понимать, что поиск в данном случае осуществляется исключительно по принципу строгого равенства.
Если мы передаем в структуру отбора переменную типа СписокЗначений, система не начинает перебирать элементы этого списка. Вместо этого она пытается найти такую строку в таблице значений, в ячейке которой лежит точно такой же объект (экземпляр) СписокЗначений. В 99% случаев в колонках таблицы хранятся ссылки (например, СправочникСсылка.Номенклатура), а не объекты коллекций, поэтому поиск возвращает пустой массив.
Рассмотрим пример ошибочного кода:
// Это не будет работать для поиска элементов списка
СписокНужныхТоваров = Новый СписокЗначений;
СписокНужныхТоваров.Добавить(Ссылка1);
СписокНужныхТоваров.Добавить(Ссылка2);
Отбор = Новый Структура("Номенклатура", СписокНужныхТоваров);
// Метод ищет ячейку, где хранится сам объект "СписокЗначений", а не Ссылка1 или Ссылка2
НайденныеСтроки = ТЗ.НайтиСтроки(Отбор);
В типовых конфигурациях, построенных на базе БСП (Библиотеки стандартных подсистем), часто встречается функция ОтобратьСтрокиПоКритериям(). Выясним причину ее популярности: она позволяет использовать мощь движка отборов 1С без написания текста запроса на языке SQL. Под капотом этот метод использует объект ПостроительОтчета.
Разберем по шагам, как реализовать такой поиск самостоятельно:
ПостроительОтчета.ОписаниеИсточникаДанных.ВСписке.Посмотрим на пример реализации:
Построитель = Новый ПостроительОтчета;
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТЗ);
// Добавляем отбор по нужной колонке
НовыйОтбор = Построитель.Отбор.Добавить("Номенклатура");
НовыйОтбор.Использование = Истина;
НовыйОтбор.ВидСравнения = ВидСравнения.ВСписке;
НовыйОтбор.Значение = СписокНоменклатуры; // Наш СписокЗначений
Построитель.Выполнить();
ТЗ_Результат = Построитель.Результат.Выгрузить();
Этот способ официально "не является запросом" в контексте написания текста ВЫБРАТЬ... ИЗ..., хотя технически платформа использует похожие механизмы обработки данных в памяти.
Если нам нужно именно получить массив строк исходной таблицы (объектов СтрокаТаблицыЗначений), а не новую таблицу, как это делает построитель, можно воспользоваться методом временной пометки. Этот метод часто оказывается быстрее, чем многократный вызов НайтиСтроки() в цикле.
Проанализируем ситуацию: нам нужно быстро "отловить" все подходящие строки за один или два прохода. Алгоритм действий следующий:
ТЗ временную колонку типа Булево, например, с именем ЭтоНужнаяСтрока.НайтиСтроки() по нашей булевой колонке.Пример кода:
ТЗ.Колонки.Добавить("ЭтоНужнаяСтрока", Новый ОписаниеТипов("Булево"));
// Помечаем строки
Для Каждого СтрокаТЗ Из ТЗ Цикл
Если СписокНоменклатуры.НайтиПоЗначению(СтрокаТЗ.Номенклатура) <> Неопределено Тогда
СтрокаТЗ.ЭтоНужнаяСтрока = Истина;
КонецЕсли;
КонецЦикла;
// Теперь НайтиСтроки отработает мгновенно по точному соответствию
НужныеСтроки = ТЗ.НайтиСтроки(Новый Структура("ЭтоНужнаяСтрока", Истина));
// Не забываем удалить временную колонку, если она больше не нужна
ТЗ.Колонки.Удалить("ЭтоНужнаяСтрока");
Если ваш СписокЗначений содержит всего 2-5 элементов, а сама ТаблицаЗначений огромна, иногда эффективнее запустить цикл по самому списку и накапливать результаты поиска в общем массиве. В этом случае мы будем использовать преимущество индексации таблицы значений (если индекс предварительно создан).
Рассмотрим, как это сделать правильно:
// Создаем индекс для ускорения работы НайтиСтроки()
ТЗ.Индексы.Добавить("Номенклатура");
ОбщийМассивСтрок = Новый Массив;
Для Каждого ЭлементСписка Из СписокНоменклатуры Цикл
ПараметрыОтбора = Новый Структура("Номенклатура", ЭлементСписка.Значение);
МассивТекущихСтрок = ТЗ.НайтиСтроки(ПараметрыОтбора);
// Добавляем найденные строки в общий массив
Для Каждого НайденнаяСтрока Из МассивТекущихСтрок Цикл
ОбщийМассивСтрок.Добавить(НайденнаяСтрока);
КонецЦикла;
КонецЦикла;
Важный нюанс: этот метод эффективен только при наличии индекса. Без индекса система будет каждый раз сканировать всю таблицу целиком, что приведет к значительным задержкам на больших объемах данных.
Подведем итоги нашего анализа. Прямого способа передать список в НайтиСтроки() не существует из-за архитектурных ограничений метода. Выбор альтернативного решения зависит от ваших задач:
ПостроительОтчета или Схему Компоновки Данных.Запроса. Помещение таблицы значений во временную таблицу и выполнение запроса с условием ГДЕ Номенклатура В (&Список) будет работать в десятки раз быстрее за счет оптимизации на уровне движка БД или внутреннего менеджера временных таблиц платформы.Помните, что производительность — это ключевой фактор. Индексируйте колонки перед поиском и выбирайте тот инструмент, который лучше всего подходит под конкретный объем данных.