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