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