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

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

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

Причина проблемы: Жизненный цикл серверного вызова

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

Для сохранения данных нам необходимо использовать специализированные механизмы 1С, которые позволяют «протащить» объект через клиентский разрыв. Выясним, какие инструменты подходят для этого лучше всего.

Решение 1. Использование Временного хранилища с привязкой к форме

Это наиболее технологичный и правильный способ с точки зрения платформы 1С. Однако здесь есть критически важный нюанс: если просто вызвать ПоместитьВоВременноеХранилище(ТЗ), данные исчезнут сразу после возврата на клиента. Чтобы данные «жили» до тех пор, пока открыта форма, необходимо передавать уникальный идентификатор формы.

Рассмотрим алгоритм действий по шагам:

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

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


&НаСервере
Процедура СохранитьТаблицуВХранилище()
    // Выгружаем данные из табличной части объекта
    ТаблицаДляСохранения = Объект.ТабЧасть.Выгрузить();
    
    // Помещаем в хранилище с обязательным указанием УникальныйИдентификатор
    // Это гарантирует, что данные будут удалены только при закрытии формы
    АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаДляСохранения, ЭтаФорма.УникальныйИдентификатор);
КонецПроцедуры

&НаСервере
Процедура ВосстановитьТаблицуИзХранилища()
    Если ЭтоАдресВременногоХранилища(АдресХранилища) Тогда
        ИсходнаяТЗ = ПолучитьИзВременногоХранилища(АдресХранилища);
        Объект.ТабЧасть.Загрузить(ИсходнаяТЗ);
    КонецЕсли;
КонецПроцедуры

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

Решение 2. Использование вспомогательной колонки для фильтрации

Проанализируем ситуацию: если ваша цель — только отобразить нужные строки пользователю, не обязательно физически удалять «лишние» строки из таблицы и сохранять где-то копию. Мы можем пойти более простым путем, который часто оказывается эффективнее при работе с небольшими объемами данных.

Добавим в табличную часть скрытый реквизит (колонку) с типом Булево, например, СтрокаПроходитПоФильтру. Вместо перезагрузки всей таблицы мы будем просто помечать нужные строки и использовать стандартный ОтборСтрок.

Разберем реализацию этого метода:


&НаСервере
Процедура ПрименитьСложныйФильтр(СписокНужныхОпераций)
    // Проходим циклом по всей таблице
    Для Каждого Стр Из Объект.ТабЧасть Цикл
        // Проверяем, входит ли значение в наш список отбора
        Если СписокНужныхОпераций.Найти(Стр.Операция) <> Неопределено Тогда
            Стр.СтрокаПроходитПоФильтру = Истина;
        Иначе
            Стр.СтрокаПроходитПоФильтру = Ложь;
        КонецЕсли;
    КонецЦикла;

    // Устанавливаем фиксированную структуру отбора для элемента формы
    ЭлементОтбора = Новый ФиксированнаяСтруктура("СтрокаПроходитПоФильтру", Истина);
    Элементы.ТабЧастьИмя.ОтборСтрок = ЭлементОтбора;
КонецПроцедуры

Этот способ позволяет избежать постоянных выгрузок/загрузок (Выгрузить()/Загрузить()), что положительно сказывается на стабильности индексов строк в интерфейсе.

Решение 3. Сериализация в строку (Метод ЗначениеВСтрокуВнутр)

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

Проанализируем код автора:


&НаСервере
Процедура УстановитьФильтрДокументов(Установка = Ложь)
    Если Установка Тогда
        // Сериализуем ТЗ в строку и сохраняем в реквизит формы АдресХЗ
        АдресХЗ = ЗначениеВСтрокуВнутр(Объект.Таб.Выгрузить());
        
        // Далее выполняем фильтрацию запросом...
        Запрос = Новый Запрос;
        Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
        // ... инициализация параметров и текста запроса ...
        ТЗ = Запрос.Выполнить.Выгрузить();
        Объект.Таб.Загрузить(ТЗ);
    Иначе
        // Десериализуем строку обратно в объект ТаблицаЗначений
        Если ЗначениеЗаполнено(АдресХЗ) Тогда
            ИсходнаяТЗ = ЗначениеИзСтрокиВнутр(АдресХЗ);
            Объект.Таб.Загрузить(ИсходнаяТЗ);
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

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

Сравнение методов: что выбрать?

Рассмотрим подробнее, когда какой метод уместен:

  1. Временное хранилище (с УИД формы): Идеально для средних и больших таблиц. Данные не гоняются лишний раз между клиентом и сервером. Самый профессиональный подход.
  2. Вспомогательная колонка + ОтборСтрок: Лучший вариант для удобства пользователя. Позволяет мгновенно включать и выключать фильтр без риска потери данных в строках (например, если пользователь начал что-то редактировать).
  3. Сериализация в JSON или Внутреннюю строку: Допустимо для очень маленьких таблиц (до 100-200 строк) или в ситуациях, когда нужно сохранить состояние таблицы даже между сеансами работы (если записать строку в базу данных).
  4. Реквизит формы с типом ТаблицаЗначений: Хотя в конфигураторе можно создать такой реквизит, помните, что он доступен только на сервере. При переходе на клиента он очищается, если не преобразован в ДанныеФормыКоллекция.

Оптимизация работы с запросами

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

Также помните, что для эффективной работы запроса по большим таблицам полезно создавать индексы:


ТЗ = Объект.Таб.Выгрузить();
ТЗ.Индексы.Добавить("Операция"); // Ускоряет поиск и фильтрацию

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

← На главную