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