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

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

Часто в практике разработчика 1С возникает задача: имеется список ссылок или кодов (например, полученных из Excel или внешнего API) в определенном порядке, и нужно получить данные из базы именно в этой же последовательности. Обычное использование условия ГДЕ Ссылка В (&Список) не гарантирует никакого порядка — СУБД вернет данные так, как ей удобно в текущий момент. В этой статье мы подробно разберем, как правильно передать Таблицу Значений (ТЗ) в запрос и заставить систему выдать результат строго по нашему списку.

Почему стандартный запрос не сохраняет порядок

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

Шаг 1. Подготовка и типизация Таблицы Значений

Прежде чем передать ТЗ в запрос как параметр, ее необходимо правильно подготовить. Самое важное правило — колонки ТЗ должны быть типизированы. Если создать колонку без указания типа, платформа может передать ее в СУБД некорректно, что приведет к ошибкам при сравнении или низкой производительности.

Рассмотрим, как правильно создать и заполнить ТЗ на основе имеющегося массива кодов:


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

// Заполняем таблицу данными из вашего источника
Для Каждого ТекущийКод Из МассивКодов ИзЕкселя Цикл
    НоваяСтрока = ТЗКодов.Добавить();
    НоваяСтрока.Код = ТекущийКод;
    // Фиксируем исходный порядок
    НоваяСтрока.Порядок = ТЗКодов.Количество(); 
КонецЦикла;

Обратите внимание: использование метода ЗагрузитьКолонку() работает быстрее, чем цикл, но если нам нужно пронумеровать строки для сохранения порядка, цикл является наиболее простым и наглядным способом для версий платформы ниже 8.3.20.

Шаг 2. Использование временных таблиц в запросе

Теперь проанализируем, как использовать подготовленную ТЗ внутри запроса. Основная идея заключается в том, чтобы поместить ТЗ во временную таблицу, а затем соединить ее с таблицей справочника.


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

Запрос.УстановитьПараметр("ТЗ", ТЗКодов);
Выгрузка = Запрос.Выполнить().Выгрузить();

Важный момент: Мы используем ЛЕВОЕ СОЕДИНЕНИЕ от нашей временной таблицы к справочнику. Это гарантирует, что в результате останутся все строки из нашего исходного списка, даже если для какого-то кода номенклатура не будет найдена в базе (в этом случае поле Ссылка будет содержать NULL).

Шаг 3. Современный подход: Функция АВТОНОМЕРЗАПИСИ()

Если вы работаете на платформе версии 8.3.20 и выше, процесс значительно упрощается. Вам больше не нужно вручную добавлять колонку Порядок в коде 1С. Можно воспользоваться встроенной функцией языка запросов АВТОНОМЕРЗАПИСИ().

Посмотрим на пример современного запроса:


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

Запрос.УстановитьПараметр("ТЗ", ТЗКодов);

Функция АВТОНОМЕРЗАПИСИ() автоматически присваивает каждой строке временной таблицы порядковый номер в том виде, в котором строки поступили из параметра &ТЗ. Это делает код чище и уменьшает объем передаваемых данных.

Разбор критических ошибок и нюансов производительности

Рассмотрим несколько важных аспектов, которые часто упускают начинающие разработчики, из-за чего их запросы работают медленно или нестабильно:

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

Итоговый пример оптимального кода

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


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

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

Запрос.УстановитьПараметр("ТЗ", ТЗ);
Результат = Запрос.Выполнить().Выгрузить();

Используя этот подход, мы обеспечиваем стабильность (порядок строк всегда будет верным), высокую скорость (благодаря индексации) и корректную работу с типами данных. Это профессиональный стандарт решения подобных задач в среде 1С:Предприятие.

← На главную