Как оптимизировать динамический список номенклатуры для вывода данных из последней реализации?

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

Работа с динамическими списками в 1С — это всегда баланс между функциональностью и производительностью. Часто возникает задача вывести в списке справочника Номенклатура данные из последних документов, например, цену последней закупки или, как в нашем случае, НомерУпаковки из последней реализации. Если подойти к решению «в лоб» и просто соединить таблицу справочника с табличной частью документов, система может начать зависать. Рассмотрим подробнее, почему это происходит и как решить задачу правильно.

Проблема: Почему тормозит динамический список?

Когда мы добавляем в основной запрос динамического списка ВНУТРЕННЕЕ СОЕДИНЕНИЕ или ЛЕВОЕ СОЕДИНЕНИЕ с табличной частью (Документ.РеализацияТоваровУслуг.Товары), СУБД вынуждена обрабатывать огромный массив данных. Ситуация усугубляется, если:

  1. В базе данных сотни тысяч строк в табличных частях документов.
  2. Отсутствуют индексы по полям соединения.
  3. Используются вложенные подзапросы с агрегатными функциями (МАКСИМУМ, СГРУППИРОВАТЬ ПО) внутри основного запроса.
  4. Включен механизм RLS (ограничение прав на уровне записей), который накладывает дополнительные фильтры на каждое соединение.

Проанализируем ситуацию: платформа пытается получить данные для каждой строки справочника, выполняя поиск в многомиллионной таблице товаров. Даже если на экране всего 20 строк, неоптимальный запрос может заставить SQL-сервер сканировать таблицы целиком.

Решение 1: Оптимизация текста запроса

Если мы все же решили использовать запрос, нам необходимо минимизировать количество соединяемых строк. Рассмотрим пример запроса, который ищет последнюю дату реализации для каждой номенклатуры и вытягивает соответствующий номер упаковки. Для этого нам понадобится сложная конструкция с поиском максимальной даты и последующим соединением.

Разберем пример кода для динамического списка:


ВЫБРАТЬ
    СпрНоменклатура.Ссылка КАК Ссылка,
    СпрНоменклатура.Код КАК Код,
    СпрНоменклатура.Наименование КАК Наименование,
    РеализацияПоследняя.НомерУпаковки КАК НомерУпаковки
ИЗ
    Справочник.Номенклатура КАК СпрНоменклатура
        ЛЕВОЕ СОЕДИНЕНИЕ (
            ВЫБРАТЬ
                ТЧ.Номенклатура КАК Номенклатура,
                ТЧ.НомерУпаковки КАК НомерУпаковки,
                ТЧ.Ссылка.Дата КАК ДатаДокумента
            ИЗ
                Документ.РеализацияТоваровУслуг.Товары КАК ТЧ
            ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
                ВЫБРАТЬ
                    ТЧ_Макс.Номенклатура КАК Номенклатура,
                    МАКСИМУМ(ТЧ_Макс.Ссылка.Дата) КАК МаксДата
                ИЗ
                    Документ.РеализацияТоваровУслуг.Товары КАК ТЧ_Макс
                ГДЕ
                    ТЧ_Макс.Ссылка.Проведен
                СГРУППИРОВАТЬ ПО
                    ТЧ_Макс.Номенклатура
            ) КАК ВложенныйМакс
            ПО ТЧ.Номенклатура = ВложенныйМакс.Номенклатура
                И ТЧ.Ссылка.Дата = ВложенныйМакс.МаксДата
        ) КАК РеализацияПоследняя
        ПО СпрНоменклатура.Ссылка = РеализацияПоследняя.Номенклатура
ГДЕ
    НЕ СпрНоменклатура.ЭтоГруппа

Важный нюанс: Обязательно используйте ЛЕВОЕ СОЕДИНЕНИЕ, иначе из списка пропадет вся номенклатура, по которой еще не было продаж. Также рекомендуется ограничить выборку документов по дате (например, за последние полгода), чтобы сократить объем обрабатываемых данных.

Решение 2: Использование события ПриПолученииДанныхНаСервере

Это самый «продвинутый» и производительный способ для управляемых форм. Суть заключается в том, что основной запрос динамического списка остается простым и быстрым, а дополнительные данные подтягиваются программно только для тех строк, которые видны пользователю на экране (обычно это 20–40 строк).

Рассмотрим алгоритм реализации:

  1. В основном запросе динамического списка добавьте «пустое» поле: 0 КАК НомерУпаковки.
  2. Создайте обработчик события ПриПолученииДанныхНаСервере для таблицы формы.

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


&НаСервереБезКонтекста
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    СписокНоменклатуры = Строки.ПолучитьКлючи(); // Получаем только видимые ссылки
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   РеализацияТовары.Номенклатура КАК Номенклатура,
        |   РеализацияТовары.НомерУпаковки КАК НомерУпаковки
        |ИЗ
        |   Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТовары
        |ГДЕ
        |   РеализацияТовары.Номенклатура В(&СписокНоменклатуры)
        |   И РеализацияТовары.Ссылка.Проведен
        |УПОЯДОЧИТЬ ПО
        |   РеализацияТовары.Ссылка.Дата УБЫВ
        |ИТОГИ ПО
        |   Номенклатура";
    
    Запрос.УстановитьПараметр("СписокНоменклатуры", СписокНоменклатуры);
    
    ВыборкаНоменклатура = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    
    Пока ВыборкаНоменклатура.Следующий() Цикл
        // Берем первую (самую свежую по дате) запись из детальных записей
        ВыборкаДетальные = ВыборкаНоменклатура.Выбрать();
        Если ВыборкаДетальные.Следующий() Тогда
            // Заполняем данные в строке списка
            СтрокаСписка = Строки[ВыборкаНоменклатура.Номенклатура];
            СтрокаСписка.Данные.НомерУпаковки = ВыборкаДетальные.НомерУпаковки;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Внимание! У этого метода есть ограничение. Если ваша конфигурация работает в режиме совместимости ниже 8.3.10, событие ПриПолученииДанныхНаСервере просто не будет вызываться. Выясним причину: в старых режимах совместимости платформа еще не поддерживала этот эффективный механизм «подмешивания» данных.

Решение 3: Архитектурный подход через Регистр Сведений

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

  1. Создаем непериодический регистр сведений ПоследниеДанныеПродаж.
  2. Измерения: Номенклатура. Ресурсы: НомерУпаковки, ДатаПоследнейПродажи, ДокументПродажи.
  3. В модуле документа Реализация товаров и услуг при проведении записываем актуальные данные в этот регистр.
  4. В динамическом списке просто соединяем справочник с этим регистром.

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

Рекомендации по индексации

Независимо от выбранного способа, выясним состояние ваших индексов. Чтобы запросы к табличным частям работали быстро, поле Номенклатура в табличной части Товары должно быть проиндексировано. Проверьте это в конфигураторе: в свойствах реквизита должно стоять Индексировать.

Подводя итог, для быстрого решения при малых объемах данных подойдет оптимизация текста запроса. Для высоконагруженных систем с режимом совместимости 8.3.10 и выше идеален метод ПриПолученииДанныхНаСервере. Если же требуется максимальная производительность и возможность фильтрации — внедряйте вспомогательный регистр сведений.

← На главную