Порядок строк в результате запроса 1С: синтаксис, NULL, ссылки и скорость

· 10 мин

Содержание

Чтобы отсортировать запрос в 1С, добавьте в конец текста запроса предложение УПОРЯДОЧИТЬ ПО с полями и направлением: УПОРЯДОЧИТЬ ПО Дата УБЫВ, Номер. Направление ВОЗР (по возрастанию) действует по умолчанию, УБЫВ задаёт обратный порядок. Без этого предложения порядок строк в результате ничем не гарантирован, даже если сегодня он «выглядит правильным».

Способов задать порядок три, и выбирают их по ситуации. Сортировка в самом запросе — основной вариант: её выполняет СУБД, быстро и до выгрузки данных. Сортировка уже готовой таблицы значений методом Сортировать() нужна, когда порядок меняется после обработки в коде. Настройки порядка в СКД и динамическом списке — когда сортировку должен выбирать пользователь. Синтаксис ниже одинаков для файловой и клиент-серверной базы, конфигурация значения не имеет: это язык запросов платформы 8.3.

Синтаксис УПОРЯДОЧИТЬ ПО и место в тексте запроса

Общий вид предложения:

УПОРЯДОЧИТЬ ПО
    <Поле или выражение 1> [ВОЗР | УБЫВ | ИЕРАРХИЯ],
    <Поле или выражение 2> [ВОЗР | УБЫВ],
    ...
[АВТОУПОРЯДОЧИВАНИЕ]

Английский вариант — ORDER BY ... ASC | DESC, AUTOORDER. Оба написания платформа понимает одинаково.

📌 Место предложения в запросе строго фиксировано:

  1. ВЫБРАТЬ ... ИЗ ...
  2. ГДЕ
  3. СГРУППИРОВАТЬ ПО и ИМЕЮЩИЕ
  4. ОБЪЕДИНИТЬ [ВСЕ] со следующими запросами
  5. УПОРЯДОЧИТЬ ПО
  6. АВТОУПОРЯДОЧИВАНИЕ
  7. ИТОГИ ... ПО

Простой пример — список документов реализации за период, свежие сверху:

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

В УПОРЯДОЧИТЬ ПО можно указать псевдоним из списка выборки (Дата) или поле источника (Реализация.Дата). Поле не обязано входить в выборку: можно отсортировать по Реализация.Номер, не выводя его. Исключения — запросы с РАЗЛИЧНЫЕ, группировкой и объединением, о них ниже.

💡 В конструкторе запроса порядок задают на вкладке «Порядок»: перетащите поля в правую часть и выберите направление в колонке «Сортировка». Там же стоит флажок «Автоупорядочивание».

Сортировка по иерархии

Для иерархического справочника есть третье направление — ИЕРАРХИЯ:

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
УПОРЯДОЧИТЬ ПО
    Номенклатура.Ссылка ИЕРАРХИЯ

Результат идёт деревом: группа, за ней её подчинённые элементы и подгруппы. Удобно для печатных прайс-листов и выгрузок, где важна вложенность.

Сортировка по двум и более полям

Поля перечисляют через запятую. Платформа сортирует по первому полю, при равенстве — по второму, и так далее.

УПОРЯДОЧИТЬ ПО
    Контрагент.Наименование,
    Дата УБЫВ,
    Сумма УБЫВ

Здесь строки сгруппируются по контрагенту в алфавитном порядке. Внутри одного контрагента — от последней даты к первой. Если в один день два документа, выше будет тот, где сумма больше.

⚠️ Направление относится только к тому полю, за которым стоит. Запись УПОРЯДОЧИТЬ ПО Дата, Сумма УБЫВ сортирует дату по возрастанию, а сумму — по убыванию. Нужен убывающий порядок по обоим полям — пишите УБЫВ после каждого.

Добавляйте уникальное поле в конец

Если два документа созданы с одинаковой датой и временем, их взаимный порядок не определён. При повторном запуске строки могут поменяться местами. Для стабильного результата последним полем ставьте что-то уникальное: Ссылка или Номер.

ЗадачаПорядок полей
Журнал документовДата УБЫВ, Ссылка УБЫВ
Остатки по складамСклад.Наименование, Номенклатура.Наименование
Рейтинг клиентовСумма УБЫВ, Контрагент.Наименование
Хронология движенийПериод, Регистратор, НомерСтроки

Сортировка по ссылке и АВТОУПОРЯДОЧИВАНИЕ

Частая ловушка: УПОРЯДОЧИТЬ ПО Контрагент без уточнения. Ссылка — это внутренний идентификатор, поэтому строки выстроятся в порядке, который пользователю ничего не говорит. Он не совпадёт ни с алфавитом, ни с датой создания.

Решений два:

  • ✅ сортировать по реквизиту: Контрагент.Наименование, Документ.Дата;
  • ✅ дописать ключевое слово АВТОУПОРЯДОЧИВАНИЕ после списка полей.

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

💡 Обращение Контрагент.Наименование — это неявное соединение с таблицей справочника. На небольших выборках это незаметно, на сотнях тысяч строк — заметно. Про цену таких сортировок — в разделе о скорости.

NULL при сортировке: замена NULLS FIRST и NULLS LAST

В языке запросов 1С нет конструкций NULLS FIRST и NULLS LAST, привычных по SQL. Попытка их написать даст синтаксическую ошибку. Положение пустых значений задают вручную.

NULL появляется в результате:

  • 🔸 при левом соединении, когда справа не нашлось записи;
  • 🔸 при обращении к реквизиту через точку, если ссылка пустая или другого типа;
  • 🔸 в полях виртуальных таблиц, где нет данных за период.

Где окажется NULL без явного указания, зависит от СУБД и не должно влиять на логику кода. Задайте порядок сами.

Способ 1. Заменить NULL значением через ЕСТЬNULL

ВЫБРАТЬ
    Номенклатура.Ссылка КАК Товар,
    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
    Справочник.Номенклатура КАК Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
        ПО Номенклатура.Ссылка = Остатки.Номенклатура
УПОРЯДОЧИТЬ ПО
    Остаток УБЫВ

Товары без остатков получат 0 и уйдут в конец списка. Способ подходит, когда NULL по смыслу равен нулю или пустой строке. Имя регистра в примере условное: подставьте регистр своей конфигурации.

Способ 2. Отдельное поле-признак

Когда NULL нельзя приравнять к значению (например, дата последней оплаты: «не было оплат» и «оплата в 2001 году» — разные вещи), заведите служебное поле:

ВЫБРАТЬ
    Клиенты.Ссылка КАК Клиент,
    Оплаты.ПоследняяОплата КАК ПоследняяОплата,
    ВЫБОР
        КОГДА Оплаты.ПоследняяОплата ЕСТЬ NULL
            ТОГДА 1
        ИНАЧЕ 0
    КОНЕЦ КАК БезОплат
ИЗ
    Справочник.Контрагенты КАК Клиенты
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Оплаты КАК Оплаты
        ПО Клиенты.Ссылка = Оплаты.Контрагент
УПОРЯДОЧИТЬ ПО
    БезОплат,
    ПоследняяОплата УБЫВ

БезОплат по возрастанию — аналог NULLS LAST: сначала клиенты с оплатами, свежие сверху, затем все без оплат. Поменяйте 1 и 0 местами — получите NULLS FIRST.

Сортировка в коде: запрос, таблица значений, СКД

Порядок в тексте запроса из модуля

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

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Выборка.Наименование);
КонецЦикла;

Выбрать() отдаёт строки ровно в том порядке, который задан в запросе.

Порядок, который выбирает пользователь

Имя поля нельзя передать параметром. Запись УПОРЯДОЧИТЬ ПО &ПолеСортировки не сработает: параметр подставляется как константа, и все строки получат одно и то же значение ключа. Порядок меняют подстановкой в текст:

ПоляСортировки = Новый Соответствие;
ПоляСортировки.Вставить("ПоИмени", "Наименование");
ПоляСортировки.Вставить("ПоКоду", "Код");

Поле = ПоляСортировки.Получить(ВыбранныйВариант);
Если Поле = Неопределено Тогда
    Поле = "Наименование";
КонецЕсли;

Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ПолеСортировки", Поле);

⚠️ Подставляйте в текст только имена из своего списка, как в примере. Строку, пришедшую от пользователя или из внешней системы, напрямую вставлять в текст запроса нельзя.

Сортировка готовой таблицы значений

Если данные уже выгружены и дополнены в коде, сортируйте таблицу:

Таблица = Запрос.Выполнить().Выгрузить();
// ... расчёт дополнительных колонок ...
Таблица.Сортировать("Контрагент Возр, Сумма Убыв");

Строка параметра — список колонок через запятую с направлением Возр или Убыв. Колонки со ссылками сортируются по представлению, что медленнее, чем по примитивным значениям. Для списка значений есть методы СортироватьПоЗначению() и СортироватьПоПредставлению().

СКД и динамические списки

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

Где нужен порядокЧем задавать
Выборка в коде, печать, выгрузкаУПОРЯДОЧИТЬ ПО в запросе
Таблица после расчётов в кодеТаблицаЗначений.Сортировать()
Отчёт на СКДНастройки варианта → «Сортировка»
Список на формеНастройки динамического списка

Упорядочивание во временных таблицах и пакетах

Пакетный запрос с ПОМЕСТИТЬ создаёт временную таблицу. Порядок строк в ней не сохраняется. Таблица — это набор строк для дальнейших соединений, СУБД может прочитать её в любом порядке.

Отсюда правила:

  1. В запросе с ПОМЕСТИТЬ сортировку без ПЕРВЫЕ не пишите. Она только тратит время.
  2. Сортируйте в последнем запросе пакета, который возвращает результат.
  3. С ПЕРВЫЕ N сортировка нужна: она решает, какие именно N строк попадут в таблицу.
ВЫБРАТЬ ПЕРВЫЕ 10
    Продажи.Номенклатура КАК Товар,
    СУММА(Продажи.Сумма) КАК Выручка
ПОМЕСТИТЬ ВТ_Топ10
ИЗ
    ВТ_Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Номенклатура
УПОРЯДОЧИТЬ ПО
    Выручка УБЫВ
;

////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Топ.Товар,
    Топ.Выручка
ИЗ
    ВТ_Топ10 КАК Топ
УПОРЯДОЧИТЬ ПО
    Топ.Выручка УБЫВ

В первом запросе сортировка отбирает десятку лидеров. Во втором — выстраивает её для вывода. Без второго УПОРЯДОЧИТЬ ПО десятка будет правильная, но в случайном порядке.

Объединение запросов

При ОБЪЕДИНИТЬ предложение УПОРЯДОЧИТЬ ПО пишут один раз — после последнего запроса. Оно сортирует весь общий результат. Поля указывают по псевдонимам первого запроса объединения.

ВЫБРАТЬ Поступления.Дата КАК Дата, "Приход" КАК Вид
ИЗ Документ.ПоступлениеТоваровУслуг КАК Поступления

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ Реализации.Дата, "Расход"
ИЗ Документ.РеализацияТоваровУслуг КАК Реализации

УПОРЯДОЧИТЬ ПО
    Дата

Как сортировка влияет на скорость запроса

Сортировка не бесплатна. СУБД должна получить все строки, разложить их по ключу и только потом отдать первую. На миллионе строк это ощутимая доля времени выполнения.

📊 Что делает сортировку дорогой:

  • 🐢 сортировка по реквизиту через точку (Контрагент.Наименование) — добавляет соединение со справочником;
  • 🐢 ПРЕДСТАВЛЕНИЕ(Поле) в ключе — для составных и ссылочных полей это ещё несколько соединений;
  • 🐢 АВТОУПОРЯДОЧИВАНИЕ на ссылочных полях — те же неявные соединения, только незаметные в тексте;
  • 🐢 сортировка большой выборки, из которой потом в коде берутся первые строки.

🚀 Что ускоряет:

  • ✅ ПЕРВЫЕ N вместе с сортировкой. Если поле сортировки проиндексировано, СУБД может прочитать индекс и остановиться на N-й строке;
  • ✅ сортировка по полям, на которых уже есть индекс: у документов индексированы дата и номер, у справочников — код и наименование;
  • ✅ сортировка только в финальном запросе пакета;
  • ✅ отбор в ГДЕ до сортировки: меньше строк — меньше работы.

💡 Чтобы понять, сколько стоит сортировка в конкретном запросе, сравните время с ней и без неё в консоли запросов на копии рабочей базы. Если разница большая, проверьте, нельзя ли сортировать по полю с индексом или заранее сократить выборку.

Для реквизита, по которому часто сортируют, можно в конфигураторе выставить свойство «Индексировать». Это изменение структуры базы: на рабочей базе его вносят через обычный процесс обновления конфигурации, а не «на лету».

Ошибки при упорядочивании и как их исправить

Диагностика: выберите симптом

Ещё одна неочевидная ситуация — несовпадение порядка строк в файловой и клиент-серверной базе. Строки с одинаковым ключом, буква «ё», регистр букв и NULL могут сортироваться по-разному в разных СУБД. Если алгоритм опирается на порядок, задайте его полностью явно и проверьте на той СУБД, где работает рабочая база.

Частые вопросы

Можно ли отсортировать по полю, которого нет в ВЫБРАТЬ?
Да, если в запросе нет РАЗЛИЧНЫЕ, группировки и объединения. Например, УПОРЯДОЧИТЬ ПО Документ.Номер без вывода номера.
Можно ли сортировать по выражению?
Надёжнее вынести выражение в список выборки с псевдонимом и сортировать по псевдониму. Так запрос проще читать и отлаживать.
Сохраняется ли порядок при выгрузке результата в таблицу значений?
Да. Метод Выгрузить() передаёт строки в том порядке, который задан в УПОРЯДОЧИТЬ ПО последнего запроса.
Где писать УПОРЯДОЧИТЬ ПО, если в запросе есть ИТОГИ?
Перед ИТОГИ. Детальные записи внутри каждой группировки итогов выйдут в заданном порядке.
Чем ВОЗР отличается от отсутствия направления?
Ничем: по умолчанию сортировка идёт по возрастанию. Слово ВОЗР пишут для наглядности.