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