Как вывести самую популярную услугу контрагента в динамическом списке?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача обогащения стандартных списков аналитическими данными. Один из классических примеров — вывод в списке контрагентов колонки с их «любимой услугой» (той, которая оказывалась данному клиенту чаще всего). На первый взгляд задача кажется простой, но при реализации в Динамическом списке мы сталкиваемся с рядом технических ограничений: от производительности до невозможности использования временных таблиц в простом тексте запроса. Разберем детально, как решить эту задачу правильно.

Анализ проблемы и постановка задачи

Допустим, у нас есть справочник Контрагенты и регистр накопления Продажи (обороты). Нам нужно для каждого контрагента найти одну запись из регистра продаж, которая имеет максимальное значение ресурса Количество. Если таких записей несколько (например, две разные услуги оказывались одинаковое количество раз), нам нужно выбрать любую одну из них, чтобы избежать дублирования строк в динамическом списке.

Важно понимать: Динамический список — это инструмент, который должен работать быстро. Если мы напишем тяжелый запрос, который при каждом скроллинге будет пересчитывать все продажи компании за несколько лет, пользователи столкнутся с «зависанием» интерфейса.

Решение 1. Реализация через сложный запрос (имитация Top-1 в группе)

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

Рассмотрим пример запроса, который можно адаптировать для динамического списка:


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

В этом запросе мы соединяем таблицу оборотов саму с собой по условию «количество меньше, чем в другой записи». Если условие ПродажиОбороты_Макс.Контрагент ЕСТЬ NULL выполняется, значит, записи с еще большим количеством просто не существует — это и есть наш максимум.

Нюанс: Если у контрагента есть две услуги с абсолютно одинаковым максимальным количеством, этот запрос вернет обе. Чтобы этого избежать в динамическом списке, необходимо добавить группировку по контрагенту и выбрать МАКСИМУМ(Номенклатура).

Решение 2. Архитектурный подход (Кэширование данных)

Проанализируем ситуацию: продажи за прошлые периоды меняются редко. Заставлять СУБД вычислять «любимую услугу» в режиме реального времени при каждом открытии формы — нерационально. Профессиональный подход заключается в использовании вспомогательного Регистра сведений.

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

Посмотрим, как упростится запрос динамического списка в этом случае:


ВЫБРАТЬ
    СправочникКонтрагенты.Ссылка КАК Ссылка,
    СправочникКонтрагенты.Наименование КАК Наименование,
    ЕСТЬNULL(КэшУслуг.Услуга, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК ЛюбимаяУслуга
ИЗ
    Справочник.Контрагенты КАК СправочникКонтрагенты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЛюбимыеУслугиКонтрагентов КАК КэшУслуг
        ПО СправочникКонтрагенты.Ссылка = КэшУслуг.Контрагент

Этот вариант будет работать мгновенно, независимо от объема данных в регистре продаж.

Решение 3. Использование Схемы компоновки данных (СКД)

Если нам все же нужно вычислять данные «на лету», но мы хотим использовать временные таблицы для читаемости кода, мы можем переключить динамический список в режим Произвольного запроса и выбрать в качестве основного источника Схему компоновки данных.

Разберем, почему это важно: обычный текст запроса динамического списка не поддерживает конструкцию ПОМЕСТИТЬ. Однако, если запрос написан внутри СКД, привязанной к списку, платформа сама управляет временными таблицами. Мы можем сначала подготовить таблицу с количеством продаж, а затем выбрать из нее максимумы.

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

Практические советы по оптимизации

Выбор конкретного метода зависит от ваших требований к актуальности данных и производительности системы. Для учебных задач подойдет решение с хитрым соединением, для реального бизнеса — кэширование в регистр сведений.

← На главную