Чи дійсно можливо, що замовлення не буде гарантоване для цієї конкретної надлишкової похідної таблиці?


12

Я натрапив на це питання у розмові в Twitter із Лукашем Едером .

Хоча правильною поведінкою було б застосувати пункт ORDER BY до самого зовнішнього запиту, тому що тут ми не використовуємо DISTINCT, GROUP BY, JOIN або будь-який інший пункт WHERE у самому зовнішньому запиті, чому б RDBMS просто не передав пробіл вхідні дані, як це було відсортовано за внутрішнім запитом?

SELECT * 
FROM (
    SELECT * FROM table ORDER BY time DESC
) AS t

При запуску цього прикладу на PostgreSQL, принаймні, ви отримуєте той самий план виконання як для внутрішнього запиту, так і для цього похідного прикладу таблиці, а також того ж набору результатів.

Отже, я б припустив, що Планувальник просто відкине найвіддаленіший запит, оскільки він зайвий або просто передасть результати з внутрішньої таблиці.

Хтось думає, що це може бути не так?


4
Зауважте, що ваш запит не вдасться у SQL Server, оскільки замовлення на нього не дозволено всередині похідної таблиці.
a_horse_with_no_name

Чому ти такий недовірливий? Чому б ти припускав щось? Коли ви пишете програму, яка залишає вам вибір, чи очікуєте ви, що користувачі очікують речей щодо вашого вибору? Прочитайте про оптимізацію / реалізацію логічних та фізичних запитів.
philipxy

2
"Я б припустив, що Планувальник просто відкине найвіддаленіший запит, оскільки він зайвий або просто передасть результати з внутрішньої таблиці." Ви можете так само легко припустити, що Планувальник відкине замовний пункт у внутрішньому запиті, оскільки це безглуздо в контексті.
Wildcard

MariaDB, близько 2012 року, обговорює це питання. Відсутність внутрішньогоORDER BYрезультату призводить до різної оптимізації для групових макс .
Рік Джеймс

1
Насправді ви праві для Postgres.
Ервін Брандштеттер

Відповіді:


20

У більшості баз даних досить зрозуміло про те, що ORDER BYв підзапиті є або:

  • Не дозволено: Наприклад, SQL Server, Sybase SQL Anywhere (якщо не доповнено TOPабо OFFSET .. FETCH)
  • Безглуздо: Напр. PostgreSQL, DB2 (знову ж таки, якщо не доповнено OFFSET .. FETCHабо LIMIT)

Ось приклад із посібника з LUW DB2 (міна акценту)

Пункт ORDER BY у підвідділі не впливає на порядок рядків, повернутих запитом. Пункт ORDER BY впливає лише на порядок повернених рядків, якщо він вказаний у найбільш зовнішньому повному виді.

Формулювання досить явне, як і PostgreSQL :

Якщо сортування не вибрано, рядки будуть повернуті в не визначеному порядку. Фактичний порядок у такому випадку буде залежати від типів сканування та приєднання плану та порядку на диску, але на нього не слід покладатися . Конкретне впорядкування виводу можна гарантувати лише у тому випадку, якщо чітко обраний крок сортування.

З цієї специфікації випливає, що будь-яке впорядкування, що випливає з ORDER BYпункту у виведеній таблиці, є лише випадковим і може збігатися з вашим очікуваним замовленням (що це робиться у більшості баз даних у вашому тривіальному прикладі), але було б нерозумно покладатися на це.

Бічна примітка до DB2:

Зокрема, DB2 має менш відому функціюORDER BY ORDER OF <table-designator> , яку називають , яку можна використовувати наступним чином:

SELECT C1 FROM
   (SELECT C1 FROM T1
      UNION
    SELECT C1 FROM T2
    ORDER BY C1 ) AS UTABLE
ORDER BY ORDER OF UTABLE

У цьому конкретному випадку впорядкування похідної таблиці може бути явно повторно використане у зовнішньому самому SELECT

Бічна примітка про Oracle:

Протягом багатьох років в Oracle практикою було реалізацію OFFSETсторінки, використовуючи її ROWNUM, яку можна обґрунтовано обчислити лише після замовлення отриманої таблиці:

SELECT *
FROM (
  SELECT rownum AS rn, t.* -- ROWNUM here depends on the derived table's ordering
  FROM (
    SELECT * FROM table ORDER BY time DESC
  ) t
) t
WHERE rn BETWEEN 10 AND 20

Можна обґрунтовано очікувати, що принаймні за наявності ROWNUMзапиту майбутні версії Oracle не порушать такої поведінки, щоб не зламати майже всю застарілу SQL SQL там, яка ще не перейшла на набагато бажаніші та читаний стандартний OFFSET .. FETCHсинтаксис SQL :

SELECT * FROM table ORDER BY time DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY

Meaningless: E.g. PostgreSQLповинні бути дійсно: «неблагонадійним», тому що це робить що - то значить. Рядки сортуються за внутрішнім запитом, і цей порядок зберігається на зовнішніх рівнях запитів, якщо інше не доручено або переупорядкування не підходить для додаткових операцій. Навіть якщо це лише деталізація щодо реалізації, це не має сенсу. Це можна використовувати для сортованого введення для агрегування функцій. Посібник навіть натякає: Alternatively, supplying the input values from a sorted subquery will usually work.
Ервін Брандштеттер

Цитата, яку ви додали для Postgres, насправді стосується іншого випадку: запитів взагалі немає ORDER BY.
Ервін Брандштеттер

@ErwinBrandstetter: Не соромтесь додати відповідь із цими деталями. Я особисто не погоджуюся з тим, що деталі реалізації є важливими. Щойно сьогодні я дізнався, що в старі часи люди покладалися на Oracle, завжди виконуючи відсортовану групу за операцією в Oracle 8i (я вважаю), коли раптом новіша версія ввела хеш-групу, що порушило припущення, що деякі неявні замовлення можна покластися. Іншими словами: я люблю ставити це жирними словами. Безглуздо , а не о, якщо ви знаєте хитромудрі деталі версії xyz, ви можете насправді ...
Лукаш Едер

Я вже додав відповідь. Чи ми вирішили ігнорувати нестандартну поведінку чи якісь хороші поради, які ми маємо, поруч із питанням: Чи гарантований порядок для даного запиту? Це для Postgres. Це не застосовується (або навіть не застосовується) для інших RDBMS. І це стосується всіх існуючих версій Postgres, а не лише для версії xyz Це навіть документально підтверджено (із застереженнями). Ваша цитата вводить в оману. Якщо ми хочемо ігнорувати нестандартну поведінку, ми можемо почати з Oracle, змусивши нас повірити NULL і порожню рядок - те саме. Також ортогональне питання.
Erwin Brandstetter

@ErwinBrandstetter: Цікаво, дякую за оновлення. Це гарантія, що ви посилаєтесь на документально підтверджені документи?
Лукас Едер

12

Так. Без ORDER BYпункту вихідний порядок не визначений, і планувальник запитів цілком відповідає своїй компетенції, припускаючи, що ви це знаєте і розумієте.

Він може вирішити, що оскільки зовнішній запит не визначає порядок, він може скидати впорядкування у внутрішній запит, щоб уникнути операції сортування, особливо якщо немає кластеризованого індексу або зовсім немає індексу для підтримки замовлення. Якщо цього немає зараз, це може зробити в майбутніх версіях.

Ніколи не покладайтеся на невизначену поведінку. Якщо вам потрібне конкретне замовлення, надайте ORDER BYпропозицію у відповідному місці.


Під час тестування на PostgreSQL сортування проводилося після послідовного сканування, оскільки у мене не було жодного індексу на стовпчику, використовуваному ORDER BY. Як ви думаєте, які RDBMS пропустить внутрішній запит ЗАМОВИТИ?
Влад Міхалча

5
Я не можу сказати, що я знаю, що робити , тільки що вони будь-які і всі абсолютно вільно це роблять, якщо бажають - це була б цілком прийнятна оптимізація відповідно до загальних стандартів та специфікацій продукту. SQL Server буде відхиляти запит прямо (якщо ви не включите, TOP 100%щоб поточний запит не був переносним, якщо це буде пріоритетом для вашого проекту. Оскільки Postgres підкоряється впорядкованості у внутрішньому запиті зараз, це не означає, що це буде завжди робити в майбутньому (або що насправді старіші версії), тож вам слід уникати поведінки на всякий випадок.
Девід Спіллетт

1
@VladMihalcea СУБД, яка "оптимізує" зайве ORDER BY- MariaDB: Чому ЗАМОВЛЕННЯ В ВІДЗНАЧЕННІ Ігнорується?
ypercubeᵀᴹ

6

Це сама проблема невизначеної поведінки - працює для вас, працює для мене, переформатує жорсткий диск у виробництві;)

Ми можемо зробити крок назад і сказати, що в одному сенсі ви маєте рацію - немає жодної земної причини, чому будь-яка розумна RDBMS переставляла б рядки у внутрішньому виділенні. Але це не гарантується - це означає, що в майбутньому може бути причина, і продавці можуть це зробити. Це означає, що будь-який код, який покладається на таку поведінку, знаходиться на користь змін, які постачальник може внести, які вони не підлягають жодному публічному оприлюдненню, оскільки це не є перехідною зміною від API POV.


2
Однією з причин, по якій вона може оптимізувати замовлення, є швидкість. Повернення рядків у іншому порядку може бути ефективнішим.
TomTom

2
Зокрема, сервер може використовувати паралелізм для читання таблиці. Якщо це зробити так, і немає необхідності виконувати замовлення, ви отримаєте рядки назад, однак нитки їх читають. (SQL Server насправді робить це так, що а SELECTбез ORDER BYсправді є недетермінованим, а не просто теоретично або тому, що дані змінилися.)
Jeroen Mostert,

@JeroenMostert: Невизначена поведінка тільки погіршується. Що станеться, якщо він вийшов з ладу і дельта була використана для індексації в масив?
Джошуа

2

Чи дійсно можливо, що замовлення не буде гарантоване для цієї конкретної надлишкової похідної таблиці?

Відповідь на всі існуючі на даний момент версії Postgres (які ви тестували): Ні - для цього конкретного запиту. Порядок сортування гарантований.

Людям SQL-сервера буде незручно з цим, оскільки Microsoft навіть не дозволяє ORDER BYв запитах. Порядок сортування гарантований для цього простого запиту в Postgres. ORDER BYзастосовується в підзапиті, і зовнішній запит не робить нічого, що може змінити порядок.

Посібник навіть так само натякає в главі Зведені функції :

Як альтернатива, подача вхідних значень із відсортованого підзапиту зазвичай працює.

Зауважте, це справедливо лише тоді, коли зовнішні рівні запитів не додають операцій, які можуть змінити порядок. Тож це "гарантовано" лише для простого випадку, і це не підкріплене стандартом SQL. Postgres вільний для повторного впорядкування, якщо він підходить для додаткових операцій. У разі сумнівів додайте ще одну ORDER BYдо зовнішньої SELECT. (У такому випадку внутрішній ORDER BYбуде надмірним шумом для цього простого запиту.)


Це правда, коли "table"це не простий базовий стіл, а складний вигляд або розділена таблиця? Це правда, коли план також має паралельне виконання? Чи правда це і в Postgres 10? (Я лише запитую, я не впевнений у відповіді на будь-яке з цих питань.)
ypercubeᵀᴹ

@ ypercubeᵀᴹ: Я не тестував Postgres 10 на всі ці, але я впевнений, що це правда в будь-якому випадку. Порядок застосовується і не змінюється у зовнішньому запиті для простого випадку.
Ервін Брандстеттер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.