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