При разработке отчетов и обработок часто возникает задача предоставить пользователю возможность выбирать порядок сортировки выводимых данных (например, по номеру или дате, по возрастанию или по убыванию). Нередко начинающие разработчики пытаются передать имя поля и направление сортировки в секцию УПОРЯДОЧИТЬ ПО через стандартный механизм параметров запроса, однако сталкиваются с тем, что порядок строк в результате никак не меняется.
Разберем подробно, почему возникает такая ситуация, почему параметры не подходят для управления синтаксическими конструкциями запроса и какие существуют правильные способы решения этой задачи в платформе 1С:Предприятие.
Рассмотрим типичный пример кода, который приводит к ошибке в логике работы:
// В коде модуля:
Если Объект.Сортировка = "2" Тогда
ПолеСортировки = "Номер ВОЗР";
ИначеЕсли Объект.Сортировка = "3" Тогда
ПолеСортировки = "Номер УБЫВ";
КонецЕсли;
// Текст запроса:
Запрос.Текст =
"ВЫБРАТЬ
| Док.Номер КАК Номер,
| Док.Дата КАК Дата
|ИЗ
| Документ.ПриходнаяНакладная КАК Док
|УПОРЯДОЧИТЬ ПО
| &ПолеСортировки";
Запрос.УстановитьПараметр("ПолеСортировки", ПолеСортировки);
Проанализируем, как этот запрос воспринимается платформой 1С и СУБД. Механизм параметров (конструкции, начинающиеся с символа &) предназначен исключительно для передачи значений (чисел, дат, строк, ссылок) в выражения и фильтры (секции ГДЕ, КОГДА, ИМЕЮЩИЕ). Передавать через параметры метаданные, имена таблиц, названия полей или ключевые слова (такие как УБЫВ или ВОЗР) невозможно.
Когда мы передаем строку "Номер УБЫВ" через метод УстановитьПараметр, СУБД воспринимает её как строковый литерал (константу). В итоге запрос пытается выполнить сортировку по фиксированной строке, которая одинакова для каждой записи выборки. В результате синтаксической ошибки не возникает, но и результирующий порядок строк остаётся неизменным.
Рассмотрим рабочие варианты реализации динамического упорядочивания данных.
Самый простой и популярный способ — замена маркерного текста в шаблоне запроса перед его выполнением с помощью стандартной функции СтрЗаменить.
Разберем по шагам, как это реализовать:
&ПолеСортировки или специальный маркер).Выполнить() заменяем метку на реальное имя поля.
// Формируем фрагмент текста сортировки
ТекстСортировки = "Док.Дата ВОЗР";
Если Объект.Сортировка = 2 Тогда
ТекстСортировки = "Док.Номер ВОЗР";
ИначеЕсли Объект.Сортировка = 3 Тогда
ТекстСортировки = "Док.Номер УБЫВ";
ИначеЕсли Объект.Сортировка = 4 Тогда
ТекстСортировки = "Док.Дата ВОЗР";
ИначеЕсли Объект.Сортировка = 5 Тогда
ТекстСортировки = "Док.Дата УБЫВ";
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Номер КАК Номер,
| Док.Дата КАК Дата,
| Док.Контрагент КАК Контрагент
|ИЗ
| Документ.ПриходнаяНакладная КАК Док
|УПОРЯДОЧИТЬ ПО
| &ПолеСортировки";
// Заменяем маркер на сформированное выражение сортировки
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ПолеСортировки", ТекстСортировки);
РезультатЗапроса = Запрос.Выполнить();
Если выбор пользователя предполагает вариант «Без сортировки», то секцию УПОРЯДОЧИТЬ ПО можно целиком добавлять к базовому тексту запроса динамически:
ТекстЗапроса =
"ВЫБРАТЬ
| Док.Номер КАК Номер,
| Док.Дата КАК Дата
|ИЗ
| Документ.ПриходнаяНакладная КАК Док";
// Добавляем упорядочивание только при необходимости
Если Объект.Сортировка = 2 Тогда
ТекстЗапроса = ТекстЗапроса + " УПОРЯДОЧИТЬ ПО Док.Номер ВОЗР";
ИначеЕсли Объект.Сортировка = 3 Тогда
ТекстЗапроса = ТекстЗапроса + " УПОРЯДОЧИТЬ ПО Док.Номер УБЫВ";
ИначеЕсли Объект.Сортировка = 4 Тогда
ТекстЗапроса = ТекстЗапроса + " УПОРЯДОЧИТЬ ПО Док.Дата ВОЗР";
ИначеЕсли Объект.Сортировка = 5 Тогда
ТекстЗапроса = ТекстЗапроса + " УПОРЯДОЧИТЬ ПО Док.Дата УБЫВ";
КонецЕсли;
Запрос = Новый Запрос(ТекстЗапроса);
Результат = Запрос.Выполнить();
Если требуется сохранить текст запроса статичным (например, для удобства редактирования через конструктор запросов), можно использовать выражение ВЫБОР КОГДА непосредственно внутри секции УПОРЯДОЧИТЬ ПО. В этом случае в запрос передается числовой параметр режима:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Номер КАК Номер,
| Док.Дата КАК Дата,
| Док.Контрагент КАК Контрагент
|ИЗ
| Документ.ПриходнаяНакладная КАК Док
|УПОРЯДОЧИТЬ ПО
| ВЫБОР КОГДА &Режим = 2 ТОГДА Док.Номер ИНАЧЕ NULL КОНЕЦ ВОЗР,
| ВЫБОР КОГДА &Режим = 3 ТОГДА Док.Номер ИНАЧЕ NULL КОНЕЦ УБЫВ,
| ВЫБОР КОГДА &Режим = 4 ТОГДА Док.Дата ИНАЧЕ NULL КОНЕЦ ВОЗР,
| ВЫБОР КОГДА &Режим = 5 ТОГДА Док.Дата ИНАЧЕ NULL КОНЕЦ УБЫВ";
Запрос.УстановитьПараметр("Режим", Число(Объект.Сортировка));
Результат = Запрос.Выполнить();
Обратите внимание: СУБД вычисляет выражения по очереди. Если условие в ветке не выполняется, возвращается значение NULL, и такое выражение не оказывает влияния на итоговый порядок записей.
В современной разработке на платформе 1С:Предприятие 8.3 для безопасного управления текстом запроса рекомендуется использовать встроенный объект СхемаЗапроса. Это избавляет от строковой конкатенации и защищает от синтаксических ошибок.
Посмотрим на пример работы со схемой:
БазовыйТекст =
"ВЫБРАТЬ
| Док.Номер КАК Номер,
| Док.Дата КАК Дата
|ИЗ
| Документ.ПриходнаяНакладная КАК Док";
Схема = Новый СхемаЗапроса;
Схема.УстановитьТекстЗапроса(БазовыйТекст);
Оператор = Схема.ПакетЗапросов[0].Операторы[0];
Оператор.Порядок.Очистить();
Если Объект.Сортировка = 2 Тогда
ЭлементПорядка = Оператор.Порядок.Добавить("Док.Номер");
ЭлементПорядка.Направление = НаправлениеСортировкиСхемыЗапроса.Возр;
ИначеЕсли Объект.Сортировка = 3 Тогда
ЭлементПорядка = Оператор.Порядок.Добавить("Док.Номер");
ЭлементПорядка.Направление = НаправлениеСортировкиСхемыЗапроса.Убыв;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст = Схема.ПолучитьТекстЗапроса();
Результат = Запрос.Выполнить();
Если объем данных небольшой, упорядочивание можно выполнить уже после отработки запроса средствами встроенного языка платформы:
РезультатЗапроса = Запрос.Выполнить();
ТаблицаДанных = РезультатЗапроса.Выгрузить();
// Метод Сортировать принимает строку с именами колонок и направлениями
ТаблицаДанных.Сортировать("Дата УБЫВ, Номер ВОЗР");
Для Каждого Стр Из ТаблицаДанных Цикл
Сообщить(Стр.Номер + " | " + Формат(Стр.Дата, "ДФ=dd.MM.yyyy"));
КонецЦикла;
Если Не Выборка.Проведен Тогда Продолжить;). Всегда добавляйте условия в секцию ГДЕ запроса (ГДЕ Док.Проведен = ИСТИНА), чтобы исключить передачу избыточных данных по сети с сервера СУБД.Номер у большинства документов является строковым. При сортировке строк СУБД использует лексикографический порядок (число "10" будет стоять раньше числа "2"). Если номера документов не содержат лидирующих нулей, для корректного числового порядка может потребоваться предварительная обработка или использование дополнительного реквизита.