В практике разработчика 1С часто возникает ситуация, когда таблица значений (ТЗ) передается в запрос в качестве параметра для помещения во временную таблицу, а затем выбирается обратно. Многие замечают, что порядок строк в результирующей таблице иногда совпадает с исходным, а иногда — нет. Разберемся подробно, почему это происходит, как на это влияют индексы и почему нельзя полагаться на "случайную" стабильность данных.
Первое и самое важное правило, которое мы должны усвоить: порядок записей в результате запроса без явной сортировки неопределен. Это не особенность 1С, а базовый принцип работы реляционных баз данных (SQL). С точки зрения СУБД, таблица — это неупорядоченное множество строк. Если разработчик не указал конструкцию УПОРЯДОЧИТЬ ПО, система имеет право вернуть строки в любом порядке, который на данный момент является наиболее эффективным с точки зрения производительности.
Рассмотрим стандартный пример кода, который часто вызывает вопросы:
Запрос.УстановитьПараметр("тз1", Таблица);
Запрос.Текст=
"ВЫБРАТЬ
| *
|ПОМЕСТИТЬ Вт1
|ИЗ
| &тз1 КАК тз1
|;
|
|ВЫБРАТЬ
| Вт1.*
|ИЗ
| Вт1 КАК Вт1";
Результат = Запрос.Выполнить().Выгрузить();
Проанализируем ситуацию: в данном случае Результат может как полностью совпадать по порядку с исходной Таблица, так и быть перемешанным. Выясним причину такого поведения.
Когда мы передаем таблицу значений в запрос, платформа 1С выполняет скрытые от нас действия:
Именно поэтому на небольших объемах данных мы часто видим "сохраненный" порядок — СУБД просто читает данные физически подряд с диска или из кэша. Но это поведение — побочный эффект, а не гарантия.
Ситуация меняется, когда мы начинаем использовать индексирование. В обсуждении на форуме затронут важный вопрос: влияет ли ИНДЕКСИРОВАТЬ ПО на порядок? Посмотрим на этот механизм подробнее.
Начиная с определенных релизов платформы 8.3 (в частности, активно обсуждается поведение с версии 8.3.8), при создании временной таблицы с индексами платформа 1С создает в MS SQL Server кластерный индекс по указанным полям.
Разберем, что это значит:
ПОМЕСТИТЬ Вт1 ИЗ &тз1 КАК тз1 ИНДЕКСИРОВАТЬ ПО Поле1, то данные во временной таблице будут физически отсортированы по Поле1.УПОРЯДОЧИТЬ ПО.Однако стоит учитывать, что в 1С для одной временной таблицы можно создать только один кластерный индекс (первый указанный). Все остальные индексы будут некластерными и на физический порядок хранения данных не повлияют.
Проанализируем, почему на разных СУБД поведение может отличаться. Рассмотрим популярную сейчас PostgreSQL. В этой системе понятие кластерного индекса отличается от MS SQL. В Postgres данные в таблице — это всегда "куча", а команда CLUSTER является разовой операцией и не поддерживает порядок автоматически при добавлении новых строк. Поэтому в среде PostgreSQL шанс получить "перемешанные" данные без явной сортировки гораздо выше, чем в MS SQL.
Также на порядок влияют следующие факторы:
Выяснив теоретические основы, перейдем к практическим советам. Чтобы ваш код работал стабильно и предсказуемо, следуйте этим правилам:
НомерСтроки (типа Число), заполните ее по порядку (1, 2, 3...), и в итоговом запросе укажите УПОРЯДОЧИТЬ ПО НомерСтроки.Рассмотрим пример реализации с сохранением исходного порядка:
// Подготовим ТЗ с сохранением порядка
Таблица.Колонки.Добавить("ИндексПорядка", Новый ОписаниеТипов("Число"));
Для Каждого Стр Из Таблица Цикл
Стр.ИндексПорядка = Таблица.Индекс(Стр);
КонецЦикла;
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ТЗ", Таблица);
Запрос.Текст =
"ВЫБРАТЬ * ПОМЕСТИТЬ ВТ ИЗ &ТЗ КАК ТЗ;
|ВЫБРАТЬ * ИЗ ВТ КАК ВТ УПОРЯДОЧИТЬ ПО ВТ.ИндексПорядка";
ТаблицаРезультат = Запрос.Выполнить().Выгрузить();
Таким образом, мы гарантируем, что никакие особенности оптимизатора СУБД или версии платформы не повлияют на правильность работы нашего алгоритма. Помните, что код, работающий "по счастливому стечению обстоятельств" сегодня, может сломаться завтра после обновления платформы или смены SQL-сервера.