Почему не сортируется результат запроса в 1С при использовании секции УПОРЯДОЧИТЬ ПО?

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

В практике разработки на платформе 1С:Предприятие нередко возникает ситуация, когда в тексте запроса явно прописана секция УПОРЯДОЧИТЬ ПО, однако итоговые данные на форме или в результирующей таблице выводятся не по алфавиту или вовсе в хаотичном порядке. Проанализируем причины такого поведения платформы и разберем практические способы решения этой задачи.

Главная причина: выполнение запроса внутри цикла (антипаттерн)

Если в коде алгоритма выполняется перебор некоторой коллекции (например, списка договоров или контрагентов), и на каждом шаге цикла вызывается отдельный параметризованный запрос с секцией УПОРЯДОЧИТЬ ПО, итоговый массив строк не будет отсортирован глобально. Выясним причину: секция сортировки действует строго в рамках одной выборки текущей итерации. Если во внешний цикл договоры поступают без сортировки, то и результирующая коллекция заполнится в порядке их следования.

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


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

Выполнение запросов в цикле не только нарушает порядок сортировки, но и многократно увеличивает нагрузку на СУБД из-за постоянной компиляции планов выполнения и задержек клиент-серверного взаимодействия.

Особенности сортировки по ссылочным полям и механизм АВТОУПОРЯДОЧИВАНИЕ

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

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

Поведение сортировки во временных таблицах

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

Правильное построение единого запроса

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

Разберем по шагам итоговый оптимизированный запрос:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Реализация.Ссылка КАК Расходная,
    |   Реализация.ДоговорКонтрагента КАК Договор,
    |   Реализация.Контрагент КАК Контрагент,
    |   Реализация.Контрагент.Наименование КАК КонтрагентНаименование,
    |   Реализация.Дата КАК Дата,
    |   Реализация.Номер КАК Номер,
    |   ВЫБОР
    |       КОГДА Реализация.ВалютаДокумента = &Гривна
    |           ТОГДА Реализация.СуммаДокумента * 2
    |       ИНАЧЕ Реализация.СуммаДокумента
    |   КОНЕЦ КАК СуммаР
    |ПОМЕСТИТЬ ВТ_Расходные
    |ИЗ
    |   Документ.РеализацияТоваровУслуг КАК Реализация
    |ГДЕ
    |   Реализация.Проведен = ИСТИНА
    |   И Реализация.ДоговорКонтрагента В(&МассивДоговоров)
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   ВТ_Расходные.Расходная КАК Расходная,
    |   ВТ_Расходные.Договор КАК Договор,
    |   ВТ_Расходные.Контрагент КАК Контрагент,
    |   ВТ_Расходные.КонтрагентНаименование КАК КонтрагентНаименование,
    |   ВТ_Расходные.Дата КАК Дата,
    |   ВТ_Расходные.Номер КАК Номер,
    |   ВТ_Расходные.СуммаР КАК Сумма,
    |   СУММА(ЕСТЬNULL(ВЫБОР
    |               КОГДА ПКО.ВалютаДокумента = &Гривна
    |                   ТОГДА ПКО.СуммаДокумента * 2
    |               ИНАЧЕ ПКО.СуммаДокумента
    |           КОНЕЦ, 0)) КАК Оплата,
    |   СУММА(ЕСТЬNULL(ВЫБОР
    |               КОГДА ВозвратТовары.Ссылка.ВалютаДокумента = &Гривна
    |                   ТОГДА ВозвратТовары.Сумма * 2
    |               ИНАЧЕ ВозвратТовары.Сумма
    |           КОНЕЦ, 0)) КАК Возврат
    |ИЗ
    |   ВТ_Расходные КАК ВТ_Расходные
    |   ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходныйКассовыйОрдер КАК ПКО
    |       ПО ВТ_Расходные.Расходная = ПКО.ДокументОснование
    |       И (ПКО.Проведен = ИСТИНА)
    |   ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратТоваровОтПокупателя.Товары КАК ВозвратТовары
    |       ПО ВТ_Расходные.Расходная = ВозвратТовары.ДокументПартии
    |       И (ВозвратТовары.Ссылка.Проведен = ИСТИНА)
    |СГРУППИРОВАТЬ ПО
    |   ВТ_Расходные.Расходная,
    |   ВТ_Расходные.Договор,
    |   ВТ_Расходные.Контрагент,
    |   ВТ_Расходные.КонтрагентНаименование,
    |   ВТ_Расходные.Дата,
    |   ВТ_Расходные.Номер,
    |   ВТ_Расходные.СуммаР
    |УПОРЯДОЧИТЬ ПО
    |   КонтрагентНаименование ВОЗР,
    |   Дата ВОЗР";

Запрос.УстановитьПараметр("МассивДоговоров", СписокДоговоров);
Запрос.УстановитьПараметр("Гривна", Справочники.Валюты.НайтиПоКоду("980"));

РезультатЗапроса = Запрос.Выполнить();
ТаблицаРезультат = РезультатЗапроса.Выгрузить();

Сортировка коллекции программным способом

Если данные уже выгружены в объект ТаблицаЗначений, упорядочивание можно выполнить методом Сортировать(). Проанализируем правильный синтаксис:


// Сортировка по наименованию контрагента и дате документа
ТаблицаРезультат.Сортировать("КонтрагентНаименование Возр, Дата Убыв");

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

← На главную