Как отфильтровать таблицу значений по типу данных в колонке

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

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

Почему метод НайтиСтроки не подходит для фильтрации по типу

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


Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", Тип("Строка"));
НайденныеСтроки = ТЗ_Данных.НайтиСтроки(Отбор);

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

Способ 1. Обход таблицы в цикле с удалением строк

Это самый простой и интуитивно понятный способ, который подходит для таблиц небольшого и среднего объема. Основная хитрость здесь заключается в направлении обхода. Выясним причину: если мы будем удалять строки, двигаясь от первой к последней, индексы оставшихся строк будут смещаться, что приведет к пропуску данных или ошибке выхода за границы массива. Поэтому мы рекомендуем выполнять обход с конца к началу.

Рассмотрим пример реализации:


// Предположим, ТЗ_Результат — наша исходная таблица
КоличествоСтрок = ТЗ_Результат.Количество();

Для Индекс = 1 По КоличествоСтрок Цикл
    // Вычисляем обратный индекс
    ТекущийИндекс = КоличествоСтрок - Индекс;
    ТекущаяСтрока = ТЗ_Результат[ТекущийИндекс];
    
    // Проверяем тип значения в колонке Номенклатура
    Если ТипЗнч(ТекущаяСтрока.Номенклатура) <> Тип("Строка") Тогда
        ТЗ_Результат.Удалить(ТекущаяСтрока);
    КонецЕсли;
КонецЦикла;

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

Способ 2. Использование Запроса для фильтрации ТЗ

Если таблица содержит тысячи или десятки тысяч строк, перебор в цикле может работать медленно. В таких случаях эффективнее использовать механизм запросов. Однако здесь есть важный нюанс: при передаче таблицы значений в запрос необходимо явно описать типы колонок, иначе система не сможет корректно обработать данные.

Разберем по шагам, как это сделать:

  1. Подготовим таблицу значений, убедившись, что колонки типизированы.
  2. Поместим таблицу во временную таблицу в запросе.
  3. Используем функцию ТИПЗНАЧЕНИЯ или оператор ССЫЛКА в условии ГДЕ.

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


Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

// Важно: ТЗ должна иметь описание типов для колонки Номенклатура
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЗ.Номенклатура КАК Номенклатура,
    |    ТЗ.Количество КАК Количество
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |    &ВнешняяТЗ КАК ТЗ
    |;
    |
    |ВЫБРАТЬ
    |    ВТ_Данные.Номенклатура,
    |    ВТ_Данные.Количество
    |ИЗ
    |    ВТ_Данные КАК ВТ_Данные
    |ГДЕ
    |    ТИПЗНАЧЕНИЯ(ВТ_Данные.Номенклатура) = ТИП(СТРОКА)";

Запрос.УстановитьПараметр("ВнешняяТЗ", ИсходнаяТЗ);
РезультатЗапроса = Запрос.Выполнить();
ТЗ_ТолькоСтроки = РезультатЗапроса.Выгрузить();

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

Способ 3. Метод «типизированного копирования»

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

Внимание: Этот метод коварен. Если в ячейке находится ссылка, то при копировании в строковую колонку она может превратиться в строковое представление ссылки (наименование или UUID), а не отфильтроваться. Поэтому данный способ подходит только в узких задачах или при использовании метода ЗагрузитьКолонку() с последующей очисткой «мусора».

Способ 4. Добавление вспомогательной колонки «Тип»

Если вам нужно часто фильтровать таблицу по типам в процессе работы (например, в интерактивной форме), имеет смысл добавить в таблицу служебную колонку ТипНоменклатуры с типом Тип или Число.

Рассмотрим алгоритм работы:

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

// Заполнение вспомогательной колонки
Для Каждого СтрокаТЗ Из ТЗ_Данных Цикл
    СтрокаТЗ.ТипНоменклатуры = ТипЗнч(СтрокаТЗ.Номенклатура);
КонецЦикла;

// Теперь поиск будет работать!
Отбор = Новый Структура("ТипНоменклатуры", Тип("Строка"));
НайденныеСтроки = ТЗ_Данных.НайтиСтроки(Отбор);

Резюме и рекомендации

Выясним, какой метод выбрать в зависимости от условий задачи:

Таким образом, хотя прямого способа передать тип в Структуру поиска не существует, гибкость платформы 1С позволяет решить эту задачу несколькими эффективными способами в зависимости от контекста разработки.

← На главную