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