Как отсортировать результат запроса 1С в том же порядке, в котором элементы переданы в массиве?

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

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

Однако если просто передать массив в условие В (&Массив), выводимый результат запроса окажется отсортирован не так, как в исходном файле или массиве.

Выясним причину поведения СУБД

Рассмотрим, почему стандартный запрос 1С не сохраняет последовательность элементов массива:

  1. Особенность оператора В (IN в SQL): Передача массива в условие ТелефонВФормате В (&Шаблон) лишь проверяет факт присутствия значения в списке. Система управления базами данных (СУБД) игнорирует позицию элементов внутри переданного массива.
  2. Отсутствие гарантии порядка без секции УПОРЯДОЧИТЬ ПО: Если в запросе не указана конструкция УПОРЯДОЧИТЬ ПО, СУБД возвращает записи в произвольном порядке, определяемом планом выполнения запроса, используемыми индексами и устройством физических таблиц.
  3. Невозможность обращения к индексам массива в самом запросе: В языке запросов 1С нельзя напрямую обратиться к порядковому номеру элемента массива, переданного через параметр.

Проанализируем два основных способа решения этой задачи.

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

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

Разберем по шагам создание такой таблицы и связывание её в запросе:

  1. Создаем объект ТаблицаЗначений с колонками Телефон (тип Строка) и НомерСтроки (тип Число).
  2. В цикле обходим исходный массив или файл лога и заполняем таблицу значений, фиксируя в колонке НомерСтроки текущий индекс (1, 2, 3 и т. д.).
  3. Передаем таблицу значений в параметр запроса и помещаем её во временную таблицу.
  4. Соединяем временную таблицу с целевым регистром или справочником и указываем сортировку УПОРЯДОЧИТЬ ПО ВТ_Лог.НомерСтроки.

Рассмотрим пример кода:


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

// 2. Заполняем ТЗ из массива с фиксацией порядка строк
Индекс = 1;
Для Каждого ТелефонИзЛога Из МассивНомеров Цикл
    НоваяСтр = ТЗ_Лог.Добавить();
    НоваяСтр.Телефон = ТелефонИзЛога;
    НоваяСтр.НомерСтроки = Индекс;
    Индекс = Индекс + 1;
КонецЦикла;

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

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

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

Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Пациент);
КонецЦикла;

Преимущество метода: Этот вариант полностью сохраняет повторяющиеся дубликаты из файла лога (если один номер звонил несколько раз на разных позициях) и возвращает результат строго в нужной последовательности.

Способ 2. Постобработка результата с помощью объекта Соответствие

Если задача заключается только в получении связанных данных по списку ключей без выполнения сложной бизнес-логики в базе, сортировку быстрее и проще перенести на сторону программного кода 1С.

Разберем по шагам выполнение постобработки:

  1. Выполняем стандартный запрос с условием ТелефонВФормате В (&Шаблон).
  2. Выгружаем результат выборки в объект Соответствие, где ключом будет выступать телефон, а значением — найденная ссылка (например, Пациент).
  3. Обходим исходный массив в естественном порядке и за постоянное время получаем найденные объекты из соответствия по ключу.

Посмотрим на пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	КонтактнаяИнформацияСрезПоследних.ТелефонВФормате КАК Телефон,
    |	КонтактнаяИнформацияСрезПоследних.Пациент КАК Пациент
    |ИЗ
    |	РегистрСведений.КонтактнаяИнформацияПациента.СрезПоследних(&ТекДата, ТелефонВФормате В (&Шаблон)) КАК КонтактнаяИнформацияСрезПоследних";

Запрос.УстановитьПараметр("ТекДата", ТекущаяДата());
Запрос.УстановитьПараметр("Шаблон", МассивНомеров);

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

// Заполняем соответствие для мгновенного поиска по ключу
СоответствиеПациентов = Новый Соответствие;
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
    СоответствиеПациентов.Вставить(Выборка.Телефон, Выборка.Пациент);
КонецЦикла;

// Обходим исходный массив телефонов и выводим данные в порядке файла/массива
Для Каждого ТелефонИзЛога Из МассивНомеров Цикл
    Пациент = СоответствиеПациентов[ТелефонИзЛога];
    Если Пациент <> Неопределено Тогда
        Сообщить(Пациент);
    КонецЕсли;
КонецЦикла;

Важное замечание: При выборе второго способа поиск по ключу в объекте Соответствие выполняется со скоростью $O(1)$, что обеспечивает высокую производительность даже на десятках тысяч строк.

← На главную