В практике разработки на платформе 1С:Предприятие нередко возникает ситуация, когда в тексте запроса явно прописана секция УПОРЯДОЧИТЬ ПО, однако итоговые данные на форме или в результирующей таблице выводятся не по алфавиту или вовсе в хаотичном порядке. Проанализируем причины такого поведения платформы и разберем практические способы решения этой задачи.
Если в коде алгоритма выполняется перебор некоторой коллекции (например, списка договоров или контрагентов), и на каждом шаге цикла вызывается отдельный параметризованный запрос с секцией УПОРЯДОЧИТЬ ПО, итоговый массив строк не будет отсортирован глобально. Выясним причину: секция сортировки действует строго в рамках одной выборки текущей итерации. Если во внешний цикл договоры поступают без сортировки, то и результирующая коллекция заполнится в порядке их следования.
Посмотрим на ошибочный фрагмент кода, в котором запрос вызывается для каждого договора отдельно:
Пока ВыборкаДоговоров.Следующий() Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Расходная,
| Реализация.Контрагент.Наименование КАК Контрагент
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.ДоговорКонтрагента = &Договор
|УПОРЯДОЧИТЬ ПО
| Контрагент";
Запрос.УстановитьПараметр("Договор", ВыборкаДоговоров.Ссылка);
// При выгрузке и объединении порций данных общий порядок нарушается
ТаблицаИтог.Заполнить(Запрос.Выполнить().Выгрузить());
КонецЦикла;
Выполнение запросов в цикле не только нарушает порядок сортировки, но и многократно увеличивает нагрузку на СУБД из-за постоянной компиляции планов выполнения и задержек клиент-серверного взаимодействия.
Вторая распространенная ошибка связана с тем, по какому именно типу данных выполняется упорядочивание. Рассмотрим различия между сортировкой по ссылке и по строковому представлению:
УПОРЯДОЧИТЬ ПО передано ссылочное поле (например, Реализация.Контрагент), СУБД упорядочивает записи по их внутреннему уникальному идентификатору (GUID), а не по наименованию. В результате визуальный порядок строк кажется случайным.Реализация.Контрагент.Наименование, либо задавать псевдоним этого поля в секции ВЫБРАТЬ.АВТОУПОРЯДОЧИВАНИЕ заставляет платформу автоматически подставлять поля основного представления (для справочников — наименование или код, для документов — дату и номер). Однако использовать автоупорядочивание следует с осторожностью, так как оно может создавать неявные соединения и снижать производительность на больших таблицах.Обратим внимание на работу с временными таблицами. Использование конструкции УПОРЯДОЧИТЬ ПО внутри запроса, помещающего результат во временную таблицу с помощью оператора ПОМЕСТИТЬ, не гарантирует сохранение порядка строк при последующей выборке. По стандарту SQL порядок строк гарантируется только в финальном пакете запроса, возвращающем результат.
Для гарантированной сортировки и оптимизации быстродействия перепишем алгоритм так, чтобы вся обработка выполнялась за один вызов запроса без циклов. Передадим массив договоров в качестве параметра или используем левое соединение.
Разберем по шагам итоговый оптимизированный запрос:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Расходная,
| Реализация.ДоговорКонтрагента КАК Договор,
| Реализация.Контрагент КАК Контрагент,
| Реализация.Контрагент.Наименование КАК КонтрагентНаименование,
| Реализация.Дата КАК Дата,
| Реализация.Номер КАК Номер,
| ВЫБОР
| КОГДА Реализация.ВалютаДокумента = &Гривна
| ТОГДА Реализация.СуммаДокумента * 2
| ИНАЧЕ Реализация.СуммаДокумента
| КОНЕЦ КАК СуммаР
|ПОМЕСТИТЬ ВТ_Расходные
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Проведен = ИСТИНА
| И Реализация.ДоговорКонтрагента В(&МассивДоговоров)
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Расходные.Расходная КАК Расходная,
| ВТ_Расходные.Договор КАК Договор,
| ВТ_Расходные.Контрагент КАК Контрагент,
| ВТ_Расходные.КонтрагентНаименование КАК КонтрагентНаименование,
| ВТ_Расходные.Дата КАК Дата,
| ВТ_Расходные.Номер КАК Номер,
| ВТ_Расходные.СуммаР КАК Сумма,
| СУММА(ЕСТЬNULL(ВЫБОР
| КОГДА ПКО.ВалютаДокумента = &Гривна
| ТОГДА ПКО.СуммаДокумента * 2
| ИНАЧЕ ПКО.СуммаДокумента
| КОНЕЦ, 0)) КАК Оплата,
| СУММА(ЕСТЬNULL(ВЫБОР
| КОГДА ВозвратТовары.Ссылка.ВалютаДокумента = &Гривна
| ТОГДА ВозвратТовары.Сумма * 2
| ИНАЧЕ ВозвратТовары.Сумма
| КОНЕЦ, 0)) КАК Возврат
|ИЗ
| ВТ_Расходные КАК ВТ_Расходные
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ПриходныйКассовыйОрдер КАК ПКО
| ПО ВТ_Расходные.Расходная = ПКО.ДокументОснование
| И (ПКО.Проведен = ИСТИНА)
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ВозвратТоваровОтПокупателя.Товары КАК ВозвратТовары
| ПО ВТ_Расходные.Расходная = ВозвратТовары.ДокументПартии
| И (ВозвратТовары.Ссылка.Проведен = ИСТИНА)
|СГРУППИРОВАТЬ ПО
| ВТ_Расходные.Расходная,
| ВТ_Расходные.Договор,
| ВТ_Расходные.Контрагент,
| ВТ_Расходные.КонтрагентНаименование,
| ВТ_Расходные.Дата,
| ВТ_Расходные.Номер,
| ВТ_Расходные.СуммаР
|УПОРЯДОЧИТЬ ПО
| КонтрагентНаименование ВОЗР,
| Дата ВОЗР";
Запрос.УстановитьПараметр("МассивДоговоров", СписокДоговоров);
Запрос.УстановитьПараметр("Гривна", Справочники.Валюты.НайтиПоКоду("980"));
РезультатЗапроса = Запрос.Выполнить();
ТаблицаРезультат = РезультатЗапроса.Выгрузить();
Если данные уже выгружены в объект ТаблицаЗначений, упорядочивание можно выполнить методом Сортировать(). Проанализируем правильный синтаксис:
// Сортировка по наименованию контрагента и дате документа
ТаблицаРезультат.Сортировать("КонтрагентНаименование Возр, Дата Убыв");
Важно: если передать в метод Сортировать колонку со ссылочным типом (например, Контрагент), платформа будет вызывать неявное чтение строковых представлений объектов из базы данных для каждой строки, что может привести к замедлению работы при больших объемах данных. Сортировка по явно выбранным строковым полям всегда работает быстрее и надежнее.