Можно ли считать сортировку, заданную внутри производной таблицы, гарантией порядка строк внешнего результата?
Нет. Сортировка внутри производной таблицы не гарантирует порядок строк результата внешнего запроса: гарантировать его может только сортировка на внешнем уровне. Исключение по смыслу — когда внутренняя сортировка участвует в операции, определяющей состав строк, например вместе с ограничением количества строк.
В реляционной модели таблица рассматривается как неупорядоченное множество строк. Оператор сортировки был добавлен как средство формирования представления результата для пользователя, а не как свойство самой таблицы или промежуточного набора данных.
Производная таблица сохраняет композиционность запросов: внешний запрос работает с её результатом как с таблицей. Поэтому порядок, использованный при промежуточном вычислении, обычно не является частью логического результата, если он не влияет на выбор строк.
Разработчик может поместить сортировку во внутренний запрос, увидеть ожидаемый порядок при тестировании и решить, что внешний запрос его сохранит. Это опасно: оптимизатор вправе изменить план, убрать ненужную промежуточную сортировку или выполнить операции во внешнем запросе в другом порядке.
Особенно заметен риск при добавлении соединений, агрегаций, параллельного выполнения или изменении индексов. Приложение, полагающееся на случайно сохранившийся порядок, будет получать нестабильные результаты без изменения текста запроса.
Порядок строк является частью требуемого результата только тогда, когда он задан ORDER BY в том запросе, результат которого потребляется клиентом. Если внешний запрос не содержит собственной сортировки, СУБД не обязана возвращать строки в порядке внутренней производной таблицы.
Во внутреннем запросе сортировка может быть логически или физически бесполезной: внешний запрос не обязан передавать этот порядок дальше. Надёжный вариант — перенести сортировку наружу:
Внутренняя сортировка значима, если она участвует в операции выбора строк, например при получении первых десяти событий. Но даже тогда она гарантирует состав выбранных строк, а не порядок окончательного результата; для порядка результата нужен внешний ORDER BY.
Есть и практический компромисс: внутренняя сортировка может быть нужна для оконных функций или других операций, где порядок является частью вычисления. Это не означает, что тот же порядок автоматически распространяется на внешний результат.
В отчёте нужно было показать последние события по времени. Сначала разработчик отсортировал события внутри производной таблицы, а затем внешний запрос присоединил к ним справочник типов. На тестовых данных порядок сохранялся, но после смены плана выполнения строки стали приходить в другом порядке.
Рассматривались два варианта. Сохранить внутреннюю сортировку было проще, но это не давало требуемой гарантии. Добавить внешнюю сортировку требовало небольшой дополнительной операции, зато явно фиксировало контракт результата.
Выбрали второй вариант: сортировку перенесли во внешний запрос, а внутреннюю оставили только там, где она влияла на выбор строк или вычисление. После этого порядок стал предсказуемым независимо от плана соединения и способа доступа к данным.
Да, если эта сортировка не влияет на логический результат внутреннего запроса. Например, без ограничения количества строк она обычно не меняет множество строк, поэтому внешний запрос не обязан её сохранять. Удаление такой операции является допустимым преобразованием, а не ошибкой оптимизатора.
Она может определять, какие строки попадут в результат внутреннего запроса: сначала строки упорядочиваются, затем выбирается ограниченное количество. Но внешний запрос всё равно может вернуть уже выбранные строки в любом порядке, если у него нет собственного ORDER BY.
Конкретный план может случайно выдавать строки в том же порядке: например, индексный доступ или последовательная обработка промежуточного результата могут его поддерживать. Это наблюдаемое свойство текущего плана, а не гарантированный контракт SQL; изменение данных, индексов, статистики или степени параллелизма может его нарушить.