Почему в динамическом списке 1С отображаются данные только по первому элементу параметра и как это исправить?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке управляемых форм в 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, а остальные колонки остаются пустыми.

Выясняем причину: разница между объектом Запрос и Системой компоновки данных

Причина такого глюка кроется в архитектуре динамических списков. Механизм ДинамическийСписок работает на основе Системы компоновки данных (СКД), а не прямого выполнения текста запроса через объект Запрос.

Разберем особенности работы платформы:

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

Данное поведение задокументировано разработчиками платформы: для корректного разворачивания списков в параметрах СКД динамического списка следует передавать исключительно тип Массив или ФиксированныйМассив.

Решение проблемы: правильная передача параметров

Для исправления ошибки достаточно преобразовать СписокЗначений в тип Массив перед установкой параметра в динамический список.

Рассмотрим правильный вариант программного кода:


// Извлекаем массив значений из СпискаЗначений
МассивСкладов = СписокСкладов.ВыгрузитьЗначения();

// Передаем в динамический список именно Массив
Список.Параметры.УстановитьЗначениеПараметра("Склады", МассивСкладов);
Список.Параметры.УстановитьЗначениеПараметра("Склад0", МассивСкладов[0]);
Список.Параметры.УстановитьЗначениеПараметра("Склад1", МассивСкладов[1]);
Список.Параметры.УстановитьЗначениеПараметра("Склад2", МассивСкладов[2]);

Если параметры задаются динамически в цикле, алгоритм заполнения будет выглядеть так:


МассивСкладов = Новый Массив;
Для Каждого ЭлементСписка Из СписокСкладов Цикл
    МассивСкладов.Добавить(ЭлементСписка.Значение);
КонецЦикла;

// Установка массива для условия В (&Склады)
Список.Параметры.УстановитьЗначениеПараметра("Склады", МассивСкладов);

Рекомендации по повышению производительности динамических списков

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

  1. Режим совместимости платформы: Использование временных таблиц в запросах динамических списков официально поддерживается начиная с платформы 8.3.8 (при режиме совместимости не ниже 8.3.8). В более старых режимах совместимости временные таблицы могут вызывать ошибки.
  2. Оптимизация порционной выборки: При наличии сложных агрегатных функций (СУММА, СГРУППИРОВАТЬ ПО) во временных таблицах платформа теряет возможность выполнять порционное считывание строк с сервера (по 20–50 элементов для видимой области экрана). Это приводит к тому, что при каждом скроллинге списка СУБД пересчитывает остатки по всей базе данных.
  3. Альтернативный метод получения остатков: Для высоконагруженных систем разработчики 1С рекомендуют рассчитывать остатки не в самом запросе динамического списка, а в серверном событии ПриПолученииДанныхНаСервере. В этом случае динамический список выбирает только данные основного справочника, а остатки запрашиваются одним точечным запросом исключительно для тех строк, которые выведены на экран пользователя.
← На главную