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