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

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

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

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

---

Почему группировка ломает сортировку в динамическом списке?

Чтобы понять суть проблемы, давайте заглянем «под капот» платформы 1С. Динамический список работает на базе Системы компоновки данных (СКД). Его главная особенность и преимущество — получение данных от СУБД порциями (по мере того, как пользователь прокручивает список на экране). Это позволяет мгновенно открывать списки, содержащие миллионы записей.

Когда пользователь кликает по заголовку колонки, чтобы отсортировать данные, платформа динамически модифицирует исходный SQL-запрос. Она добавляет в самый конец запроса конструкцию УПОРЯДОЧИТЬ ПО ... с именем выбранного поля.

Если в исходном тексте запроса динамического списка присутствует секция СГРУППИРОВАТЬ ПО или используются агрегатные функции (такие как СУММА, КОЛИЧЕСТВО, МАКСИМУМ и др.), то возникают следующие технические конфликты:

  1. Ограничения СКД: Согласно официальной документации 1С, динамический список накладывает жесткие ограничения на структуру запроса. Интерактивная сортировка не может быть корректно построена платформой, если на верхнем уровне запроса выполняется свертка (группировка) строк.
  2. Невозможность однозначного упорядочивания при постраничном выводе: Чтобы СУБД могла возвращать данные строго определенными порциями (пакетами), платформе требуется однозначно идентифицировать каждую порцию. Для этого к пользовательской сортировке всегда неявно добавляется сортировка по ключевым полям (как правило, по ссылке на объект — поле Ссылка). Если данные группируются, то поле Ссылка либо теряет свою уникальность, либо вообще исключается из группировки. В результате СУБД не может корректно сопоставить сортировку по произвольной колонке с уникальным ключом выборки, и платформа превентивно блокирует сортировку.

---

Решение автора: Использование вложенного запроса

Автор темы экспериментальным путем пришел к рабочему «обходному пути» (workaround). Он предложил поместить всю группировку во вложенный запрос. Давайте проанализируем, как это работает.

Предположим, изначально запрос динамического списка выглядел следующим образом (и сортировка не работала):


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

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


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

Почему это сработало?
Для механизма СКД, который управляет динамическим списком, внешний запрос теперь выглядит максимально простым. В нем нет ни ключевого слова СГРУППИРОВАТЬ ПО, ни агрегатных функций. Платформа видит обычную таблицу (пусть и виртуальную, сформированную вложенным запросом) и может без труда дописать в конец итогового SQL-запроса конструкцию УПОРЯДОЧИТЬ ПО Номенклатура или УПОРЯДОЧИТЬ ПО КоличествоОборот.

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

---

Альтернативные и более правильные решения (Best Practices от 1С)

Если мы хотим спроектировать систему правильно, надежно и с заделом на высокую производительность при росте базы данных, нам следует рассмотреть другие архитектурные подходы.

Способ 1. Настройка группировки в свойствах динамического списка

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

Разберем по шагам, как это сделать:

  1. Откроем форму, содержащую динамический список, в конфигураторе.
  2. Выделим элемент формы Динамический список и перейдем в его свойства.
  3. Найдем свойство Настройки компоновки данных и нажмем ссылку Открыть.
  4. В открывшемся окне перейдем на вкладку Группировка.
  5. Добавим поле, по которому необходимо сгруппировать данные (например, Номенклатура).

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

Способ 2. Отказ от динамического списка в пользу Таблицы значений

Динамический список предназначен для работы с большими (потенциально бесконечными) объемами данных. Если вы заранее знаете, что количество выводимых строк не превысит нескольких сотен или пары тысяч (например, список подразделений, складов, статей расходов или итоговые показатели за небольшой период), то использовать динамический список вообще не имеет смысла.

В этой ситуации гораздо эффективнее использовать обычную Таблицу значений или Дерево значений, размещенную на форме:

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

Пример кода для заполнения таблицы значений на форме:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Продажи.Номенклатура КАК Номенклатура,
        |   СУММА(Продажи.Количество) КАК КоличествоОборот
        |ИЗ
        |   РегистрНакопления.Продажи КАК Продажи
        |СГРУППИРОВАТЬ ПО
        |   Продажи.Номенклатура";
        
    РезультатЗапроса = Запрос.Выполнить();
    
    // Загружаем данные в реквизит формы "ТаблицаПродаж"
    ТаблицаПродаж.Загрузить(РезультатЗапроса.Выгрузить());
    
КонецПроцедуры

Преимущество метода: Сортировка по любой колонке в обычной таблице на форме выполняется мгновенно силами самой платформы на стороне клиента, без каких-либо обращений к серверу и СУБД.

Способ 3. Денормализация данных с использованием Регистров сведений

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

Для этого применяется паттерн денормализации:

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

Этот архитектурный подход гарантирует максимальную производительность системы (высокий FPS интерфейса при прокрутке), бесперебойную работу всех видов сортировок и фильтров, а также полное соответствие стандартам производительности «1С:Совместимо».

Способ 4. Очистка свойства «Основная таблица»

В свойствах динамического списка есть очень важное поле — Основная таблица. Если в нем указан конкретный справочник или документ, платформа применяет строгие правила оптимизации и накладывает ограничения на сортировку при группировках.

Если мы полностью очистим поле «Основная таблица» в свойствах динамического списка, то:

---

Резюме и рекомендации

Мы проанализировали проблему неработающей сортировки при использовании группировок в динамическом списке и рассмотрели различные варианты ее решения:

← На главную