Почему в запросе 1С сбивается порядок строк без явной сортировки и как это исправить?

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

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

Рассмотрим типовой пример запроса к табличной части документа, в котором не указана секция сортировки:


ВЫБРАТЬ
    Док.НомерСтроки КАК НомерСтроки,
    Док.Номенклатура.НаименованиеПолное КАК Номенклатура,
    Док.ЕдиницаИзмерения.Наименование КАК ЕдиницаИзмерения,
    Док.ЕдиницаИзмерения.ЕдиницаПоКлассификатору.Код КАК ЕдиницаИзмеренияКод,
    Док.Количество КАК Количество
ИЗ
    Документ.НалоговаяНакладная.Товары КАК Док
ГДЕ
    Док.Ссылка = &Ссылка

На первый взгляд кажется, что строки должны вернуться строго по возрастанию поля НомерСтроки. Однако на практике строки часто оказываются перемешанными. При этом, если исключить из выборки обращение к реквизиту Док.ЕдиницаИзмерения.ЕдиницаПоКлассификатору.Код, результат снова начинает возвращаться в исходном порядке.

Выясним причину такого поведения СУБД и платформы

Проанализируем технические процессы, происходящие на уровне СУБД и механизма трансляции запросов платформы 1С:

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

Важно понимать: совпадение порядка строк в выборке без явного указания сортировки является лишь случайным следствием текущего плана выполнения СУБД. После обновления статистики, реструктуризации таблиц или изменения объема данных порядок строк может измениться в любой момент.

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

Основным и наиболее надежным решением является явное указание инструкции УПОРЯДОЧИТЬ ПО в конце запроса. Добавим упорядочивание по номеру строки документа:


ВЫБРАТЬ
    Док.НомерСтроки КАК НомерСтроки,
    Док.Номенклатура.НаименованиеПолное КАК Номенклатура,
    Док.ЕдиницаИзмерения.Наименование КАК ЕдиницаИзмерения,
    Док.ЕдиницаИзмерения.ЕдиницаПоКлассификатору.Код КАК ЕдиницаИзмеренияКод,
    Док.Количество КАК Количество
ИЗ
    Документ.НалоговаяНакладная.Товары КАК Док
ГДЕ
    Док.Ссылка = &Ссылка
УПОРЯДОЧИТЬ ПО
    НомерСтроки ВОЗР

Если в запросе используются ссылочные поля и важно упорядочить их с учетом системных представлений и иерархии, можно применить ключевое слово АВТОУПОРЯДОЧИВАНИЕ, однако для сохранения физической последовательности строк табличной части документа всегда следует явно указывать УПОРЯДОЧИТЬ ПО Док.НомерСтроки.

Способ 2. Программная сортировка выгруженного результата

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


РезультатЗапроса = Запрос.Выполнить();
ТаблицаДанных = РезультатЗапроса.Выгрузить();
ТаблицаДанных.Сортировать("НомерСтроки Возр");

Для Каждого Стр Из ТаблицаДанных Цикл
    // Обработка строк с гарантированным сохранением порядка
КонецЦикла;

Способ 3. Переопределение запроса через механизм расширений

В современных типовых конфигурациях при обнаружении подобных ошибок в типовых алгоритмах формирования печатных форм рекомендуется использовать механизм расширения конфигурации. Разберем, как исправить алгоритм через перехват метода с помощью аннотации &ИзменениеИКонтроль или &Вместо:


&Вместо("СформироватьПечатнуюФорму")
Функция Расш1_СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати)
    // В переопределенном коде запроса добавляем секцию сортировки
    // УПОРЯДОЧИТЬ ПО Док.НомерСтроки
    // ...
КонецФункции

Рекомендации по оптимизации запросов с разыменованием полей

Для обеспечения максимального быстродействия и предсказуемости планов выполнения СУБД рекомендуется придерживаться стандартов разработки платформы 1С:

  1. Избегайте цепочек разыменования через две и более точки: Конструкция вида Док.ЕдиницаИзмерения.ЕдиницаПоКлассификатору.Код создает избыточные неявные соединения. Если требуется получить вложенные реквизиты, используйте явные секции ЛЕВОЕ СОЕДИНЕНИЕ.
  2. Применяйте временные таблицы: При сложной логике выборки оптимально сначала поместить табличную часть документа во временную таблицу с фильтром по ссылке, проиндексировать ее по ключевым полям связи, а затем соединять с таблицами справочников.
  3. Всегда контролируйте детерминированность: Если результат работы алгоритма зависит от очередности записей, секция УПОРЯДОЧИТЬ ПО обязательна к применению.
← На главную