Можно ли использовать таблицу значений в структуре для фильтрации данных в 1С?

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

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

Анализ проблемы: Почему НайтиСтроки не работает с коллекциями

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

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


ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("ПериодЗадолженности", ПредыдущийМесяц);
ПараметрыОтбора.Вставить("Поставщик", Поставщики); // Где "Поставщики" — это ТаблицаЗначений
НайденныеСтроки = ИсходнаяТаблица.НайтиСтроки(ПараметрыОтбора);

Проблема заключается в том, что механизм сравнения 1С при выполнении метода НайтиСтроки проверяет равенство ссылок или примитивных типов. Когда система доходит до ключа Поставщик, она пытается найти в ИсходнаяТаблица строку, где в колонке поставщика хранится ссылка на объект того же типа и с тем же адресом в памяти, что и переданная ТаблицаЗначений. Поскольку в колонке "Поставщик" обычно хранятся ссылки на справочник, а не коллекции, поиск всегда будет возвращать пустой результат. Даже если вы замените таблицу на СписокЗначений, метод не поймет, что нужно искать "вхождение в список" — он будет искать равенство самому объекту списка.

Нюанс сериализации при передаче между клиентом и сервером

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

Решение 1: Использование Построителя запроса для фильтрации

Как справедливо отмечено в обсуждении, наиболее эффективным способом наложить сложный отбор (в том числе "В списке") на таблицу значений без использования SQL-запроса к базе данных является ПостроительЗапроса. Этот объект умеет работать с внешними источниками данных в памяти.

Проанализируем алгоритм работы с построителем:

  1. Создаем объект ПостроительЗапроса.
  2. Назначаем нашу таблицу значений источником данных через объект ОписаниеИсточникаДанных.
  3. Добавляем необходимые отборы, включая поиск по списку или массиву.

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


Построитель = Новый ПостроительЗапроса;
// Оборачиваем таблицу значений в описание источника
Построитель.ИсточникДанных = Новый ОписаниеИсточникаДанных(ИсходнаяТаблица);

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

НовыйОтбор = Построитель.Отбор.Добавить("Поставщик");
НовыйОтбор.ВидСравнения = ВидСравнения.ВСписке;
НовыйОтбор.Значение = МассивПоставщиков;
НовыйОтбор.Использование = Истина;

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

Этот метод работает значительно быстрее, чем ручной перебор таблицы в цикле, так как механизмы фильтрации внутри ПостроителяЗапроса оптимизированы на уровне платформы.

Решение 2: Система компоновки данных (СКД)

Если требуется еще более гибкая фильтрация с учетом пользовательских настроек или иерархии, стоит рассмотреть программное использование СКД. Вы можете программно создать схему компоновки, где в качестве набора данных будет выступать объект (ваша таблица значений). Это позволяет реализовать любые виды сравнения, доступные в 1С, включая сложные логические условия "И/ИЛИ".

Решение 3: Использование временных таблиц в запросе

Несмотря на то что в исходном обсуждении вариант с запросом был нежелателен, с технической точки зрения это "золотой стандарт" разработки. Если данные находятся на сервере, правильнее всего передать таблицу значений в Запрос как параметр.

Выясним причину, почему это эффективно:

  1. Таблица значений передается в запрос и помещается во временную таблицу.
  2. Система автоматически индексирует данные, если это указано в тексте запроса.
  3. Сравнение происходит на уровне SQL (или внутреннего механизма работы с ВТ), что исключает лишние итерации в коде 1С.

Пример кода для такого подхода:


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

// Помещаем таблицу-фильтр во временную таблицу
Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Фильтр ИЗ &ТаблицаФильтр КАК ТаблицаФильтр";
Запрос.УстановитьПараметр("ТаблицаФильтр", Поставщики);
Запрос.Выполнить();

// Теперь выполняем основной поиск через соединение или условие "В"
Запрос.Текст = 
"ВЫБРАТЬ * ИЗ &ОсновнаяТаблица КАК ОсновнаяТаблица
|ГДЕ ОсновнаяТаблица.Поставщик В (ВЫБРАТЬ Поставщик ИЗ ВТ_Фильтр)";
Запрос.УстановитьПараметр("ОсновнаяТаблица", ИсходнаяТаблица);

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

Практический совет по ускорению поиска

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

Рассмотрим, как добавить индекс:


ИсходнаяТаблица.Индексы.Добавить("Поставщик");
// Теперь вызовы метода Найти() или НайтиСтроки() по этой колонке будут работать быстрее

Важный момент: Индексация имеет смысл только в том случае, если поиск по таблице выполняется многократно. Само создание индекса требует ресурсов процессора и памяти.

Подведем итоги

Использовать ТаблицуЗначений в Структуре можно для транспортировки данных внутри сервера, но использовать эту структуру для прямого поиска методом НайтиСтроки нельзя. Для фильтрации "по списку" или по сложным критериям всегда выбирайте ПостроительЗапроса или полноценный Запрос с использованием временных таблиц. Эти инструменты обеспечивают корректное сравнение типов и высокую производительность системы.

← На главную