Как выполнить отбор строк в таблице значений по массиву значений в 1С?

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

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

Почему метод НайтиСтроки не работает с массивом значений?

Проанализируем механику работы метода НайтиСтроки(). Данный метод принимает структуру отбора, где имя свойства соответствует имени колонки, а значение свойства — искомому значению. Метод НайтиСтроки() выполняет строгое сравнение на равенство (=) переданного значения со значением в ячейке таблицы.

Когда мы передаем в структуру массив:


Отбор = Новый Структура;
Отбор.Вставить("_Счет", МассивСчетовСПроцентами);
Строки = Выборка.НайтиСтроки(Отбор);

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

Рассмотрим рабочие альтернативные способы отбора строк таблицы по набору значений.

Вариант 1. Использование объекта Запрос на сервере

Если код выполняется на стороне сервера, наиболее гибким решением является передача таблицы значений во временную таблицу с последующей фильтрацией через условие ГДЕ ... В (&Массив).

Разберем пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Таб._Счет КАК _Счет,
    |    Таб.Сумма КАК Сумма
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |    &ТаблицаЗначений КАК Таб
    |;
    |
    |ВЫБРАТЬ
    |    ВТ._Счет КАК _Счет,
    |    ВТ.Сумма КАК Сумма
    |ИЗ
    |    ВТ_Данные КАК ВТ
    |ГДЕ
    |    ВТ._Счет В (&МассивСчетов)";

Запрос.УстановитьПараметр("ТаблицаЗначений", Выборка);
Запрос.УстановитьПараметр("МассивСчетов", МассивСчетовСПроцентами);

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

Особенности этого подхода:

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

Вариант 2. Циклический поиск с предварительной индексацией таблицы

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

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

Посмотрим на пример кода:


// Создаем индекс по колонке для быстрого поиска O(1)
Выборка.Индексы.Добавить("_Счет");

МассивРезультат = Новый Массив;
Отбор = Новый Структура("_Счет");

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

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

Вариант 3. Фильтрация через объект Соответствие за один проход

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

Разберем по шагам данный алгоритм:

  1. Помещаем искомые элементы массива в ключи объекта Соответствие.
  2. Выполняем один последовательный проход по таблице значений.
  3. Проверяем наличие значения ячейки в соответствиях.

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

// Шаг 2. Линейный отбор строк исходной таблицы
ИтоговыеСтроки = Новый Массив;
Для Каждого Стр Из Выборка Цикл
    Если ФильтрСчетов[Стр._Счет] <> Неопределено Тогда
        ИтоговыеСтроки.Добавить(Стр);
    КонецЕсли;
КонецЦикла;

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

Вариант 4. Использование Построителя Запроса

Еще один способ — использование объекта ПостроительЗапроса с источником данных типа ОписаниеИсточникаДанных. Этот способ позволяет программно наложить отбор по виду сравнения ВСписке.


// Формируем список значений для передачи в отбор построителя
СписокСчетов = Новый СписокЗначений;
СписокСчетов.ЗагрузитьЗначения(МассивСчетовСПроцентами);

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

ЭлементОтбора = Построитель.Отбор.Добавить("_Счет");
ЭлементОтбора.ВидСравнения = ВидСравнения.ВСписке;
ЭлементОтбора.Значение = СписокСчетов;
ЭлементОтбора.Использование = Истина;

Построитель.Выполнить();
ОтфильтрованнаяТаблица = Построитель.Результат.Выгрузить();

Обратите внимание, что объект ПостроительЗапроса доступен только в серверном контексте либо в толстом клиенте.

Сводная рекомендация по выбору решения

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