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