Как сохранить порядок элементов из Списка Значений в результате запроса 1С?

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

При разработке отчетов и обработок в 1С часто возникает ситуация, когда пользователь выбирает определенные элементы (например, номенклатуру) в СписокЗначений в конкретном порядке. Однако при передаче этого списка в запрос в качестве параметра (ГДЕ Ссылка В (&Список)), результат возвращается неупорядоченным или отсортированным по умолчанию (по коду, наименованию или ссылке). Рассмотрим, как заставить систему уважать выбор пользователя и выводить данные именно в том порядке, в котором они были добавлены в исходный список.

Метод 1. Использование временной таблицы с явным полем порядка

Это наиболее надежный «классический» способ, который позволяет выполнить сортировку непосредственно на стороне SQL-сервера. Проблема использования простого параметра типа СписокЗначений заключается в том, что внутри запроса он превращается в обычный массив, не имеющий индексов порядка строк. Для решения мы преобразуем список в ТаблицуЗначений и добавим ей колонку с индексом.

Разберем этот процесс по шагам:

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

Проанализируем пример кода, реализующий эту логику:


// 1. Подготовка данных
ТЗ_Порядок = Новый ТаблицаЗначений;
ТЗ_Порядок.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗ_Порядок.Колонки.Добавить("ИндексСортировки", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 0)));

Для Каждого ЭлементСписка Из СписокПользователя Цикл
    НоваяСтрока = ТЗ_Порядок.Добавить();
    НоваяСтрока.Номенклатура = ЭлементСписка.Значение;
    НоваяСтрока.ИндексСортировки = СписокПользователя.Индекс(ЭлементСписка);
КонецЦикла;

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

Запрос.УстановитьПараметр("ТЗ_Порядок", ТЗ_Порядок);
Запрос.УстановитьПараметр("Период", ТекущаяДата());

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

Важный нюанс: Обратите внимание на использование ЛЕВОЕ СОЕДИНЕНИЕ. Если мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ, и по какой-то номенклатуре в базе не окажется цен, эта строка просто пропадет из результата, и пользователь не увидит даже пустую строку в своем списке.

Метод 2. Сортировка результата в памяти (после выполнения запроса)

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

Выясним, как это реализовать:

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

Рассмотрим программную реализацию:


// Предположим, запрос уже выполнен и результат в переменной ТЗ_Результат
ТЗ_Результат.Колонки.Добавить("Порядок", Новый ОписаниеТипов("Число"));

Для Каждого СтрокаТЗ Из ТЗ_Результат Цикл
    // Ищем элемент в исходном списке пользователя
    ЭлементСписка = СписокПользователя.НайтиПоЗначению(СтрокаТЗ.Номенклатура);
    Если ЭлементСписка <> Неопределено Тогда
        СтрокаТЗ.Порядок = СписокПользователя.Индекс(ЭлементСписка);
    КонецЕсли;
КонецЦикла;

// Выполняем сортировку по индексу
ТЗ_Результат.Сортировать("Порядок Возр");

Этот метод часто оказывается быстрее, если список объектов невелик (до нескольких тысяч), так как он избавляет SQL-сервер от необходимости обрабатывать дополнительные временные таблицы и связи.

Метод 3. Динамическое формирование условия УПОРЯДОЧИТЬ ПО ВЫБОР

Если количество элементов в списке строго ограничено и невелико (например, всегда до 10 позиций), можно применить «хитрый» способ с динамическим формированием текста запроса. Мы можем использовать конструкцию ВЫБОР КОГДА... ТОГДА прямо в секции упорядочивания.

Посмотрим на пример логики построения такого запроса:


ТекстСортировки = "УПОРЯДОЧИТЬ ПО ВЫБОР ";
Для Индекс = 0 По СписокПользователя.Количество() - 1 Цикл
    ИмяПараметра = "Пар" + Индекс;
    ТекстСортировки = ТекстСортировки + "КОГДА Номенклатура = &" + ИмяПараметра + " ТОГДА " + Индекс + " ";
    Запрос.УстановитьПараметр(ИмяПараметра, СписокПользователя[Индекс].Значение);
КонецЦикла;
ТекстСортировки = ТекстСортировки + "ИНАЧЕ 99999 КОНЕЦ";

Запрос.Текст = "ВЫБРАТЬ Номенклатура, Цена ИЗ ... " + ТекстСортировки;

Преимущества: Не требует создания временных таблиц и изменения структуры результата запроса.
Недостатки: Текст запроса становится трудночитаемым, а при очень большом списке может быть достигнуто ограничение SQL по количеству условий в CASE.

Специфические моменты и ограничения

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

Резюме: Для большинства задач наиболее профессиональным и универсальным считается Метод 1 (Временная таблица с индексом). Он полностью переносит нагрузку на сервер БД и позволяет легко обрабатывать дубли и пустые значения.

← На главную