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

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

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

Ограничения стандартного метода НайтиСтроки()

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

  1. Поиск только на равенство: Метод НайтиСтроки() работает исключительно по условию «равно». Мы не можем передать туда условие «не равно» или «заполнено».
  2. Проблема пустых значений: Чтобы найти заполненные строки через этот метод, нам пришлось бы знать все возможные комбинации заполненных ссылок, что невозможно.
  3. Индексация: Хотя метод поддерживает работу с индексами ТЗ (которые добавляются через ТЗ.Индексы.Добавить("Колонка1, Колонка2")), индекс помогает только при поиске конкретного значения.

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

Решение 1: Использование запроса (Самый быстрый способ)

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


// Подготовим таблицу значений (например, выгрузим из формы)
ТаблицаДанных = Объект.Товары.Выгрузить();

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

Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("ТЗ", ТаблицаДанных);

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

Почему это работает быстрее? Во-первых, платформа оптимизирует работу с временными таблицами в СУБД. Во-вторых, если в запросе вам потребуется обратиться к реквизитам документов (например, Реализация.Дата), запрос сделает это одним махом через ЛЕВОЕ СОЕДИНЕНИЕ, в то время как в цикле система будет делать отдельный микро-запрос к базе на каждой итерации (проблема N+1 запросов).

Решение 2: Перебор в цикле с прямой проверкой

Если таблица небольшая (до нескольких сотен строк) и все необходимые данные уже содержатся в колонках таблицы (вам не нужно обращаться «через точку» к базе данных), обычный цикл может оказаться проще в написании и не сильно уступить в скорости. Проанализируем пример такого кода:


МассивПодходящихСтрок = Новый Массив;

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

Важный момент: используйте ЗначениеЗаполнено(), так как это универсальная функция, которая корректно определяет пустые ссылки, пустые строки и Неопределено.

Решение 3: Оптимизация через вспомогательную колонку

Рассмотрим ситуацию, когда таблица заполняется динамически или в несколько этапов. Чтобы не перебирать всю таблицу каждый раз, мы можем добавить техническую колонку ЭтоЗаполненнаяСтрока с типом Булево.

Выясним преимущества этого подхода:

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

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

Как избежать замедления при работе с формами?

Проанализируем важный технический нюанс. Если ваша таблица находится на форме (тип ДанныеФормыКоллекция), прямая работа с ней на сервере всегда медленнее, чем с объектом ТаблицаЗначений.

Разберем алгоритм «максимального ускорения»:

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

Важное замечание по типам данных: Чтобы запрос из Решения 1 работал корректно, колонки вашей ТЗ должны быть строго типизированы. Если колонка имеет составной тип или тип «Любая ссылка», СУБД не сможет эффективно построить план запроса.

Подводя итог, для простой фильтрации «в памяти» используйте цикл с ЗначениеЗаполнено(), а для сложной обработки связанных данных или огромных массивов — временные таблицы в запросах. Это обеспечит вашему коду масштабируемость и высокую скорость работы.

← На главную