SQL Server применяет ORDER BY .. OFFSET только к подзапросу UNION

В SQL Server 2022 (и более ранних версиях) это ведет себя так, как я и ожидал, в соответствии со стандартом SQL:

select i
from (values (1)) as t (i)
union 
select i
from (values (2), (3)) as t (i)
order by i
offset 0 rows
fetch next 1 rows only

Он производит:

|i  |
|---|
|1  |

Итак, как и везде, предложение ORDER BY .. OFFSET применяется к результату объединения. Однако когда я вставляю приведенный выше запрос в производную таблицу или CTE, семантика, похоже, меняется (предположим, что фактический запрос намного сложнее):

select *
from (
  select i
  from (values (1)) as t (i)
  union 
  select i
  from (values (2), (3)) as t (i)
  order by i
  offset 0 rows
  fetch next 1 rows only
) t;

Это производит:

|i  |
|---|
|1  |
|2  |

Теперь предложение ORDER BY .. OFFSET применяется только ко второму подзапросу UNION. Это заданное поведение? Кажется, я не могу найти авторитетную информацию в документах ORDER BY, которые гласят:

В запросе, использующем операторы UNION, EXCEPT или INTERSECT, ORDER BY допускается только в конце инструкции. Это ограничение применяется только в том случае, если вы указываете UNION, EXCEPT и INTERSECT в запросе верхнего уровня, а не в подзапросе. См. следующий раздел «Примеры».

Но нет ни одного примера, показывающего этот конкретный случай. Синтаксические диаграммы документации SELECT на самом деле не поддерживают ORDER BY в UNION подзапросах, только на верхнем уровне <SELECT statement>, поэтому документация также кажется устаревшей/неполной.

Это известная ошибка?

Я сделал! Выборка должна это разрешать.

Dale K 09.07.2024 22:41

Я на 99% уверен, что это ошибка SQL Server. Запрос прекрасно работает в Oracle , DB2 , PostgreSQL и MySQL.

The Impaler 10.07.2024 15:37

@TheImpaler: Это была и моя первая реакция, когда я задал вопрос. Но посмотрите мой ответ, где я показываю пример, намекающий на то, что это сделано намеренно. ORDER BY .. OFFSET кажется локальным для подзапроса UNION, а не глобальным для вывода UNION, когда UNION выполняется в подзапросе. Это не похоже на случайность/баг.

Lukas Eder 10.07.2024 17:22

@LukasEder Нужно будет еще раз просмотреть стандарт SQL (zzz), но, насколько я помню, ORDER BY/LIMIT/OFFSET применяется к «комбинированному запросу» (то есть к результату операторов набора), а не к последнему SELECT в комбинированном запросе. Как только у меня появится возможность, я пересмотрю его.

The Impaler 11.07.2024 06:03

@TheImpaler: Да, именно так это определяет стандарт SQL, и то же самое делает SQL Server для запросов верхнего уровня. Но у SQL Server (исторически Sybase T-SQL) могло быть другое представление о том, как должен работать синтаксис, в зависимости от контекста. T-SQL, как правило, не самый совместимый диалект. Опять же, тот простой факт, что в подзапросе всем UNION подзапросам (включая непоследние) разрешено иметь ORDER BY .. OFFSET предложения, намекает на наличие намерения в этом языковом дизайне, что выглядит просто странно, если только последний UNION подзапрос имеет предложение , не иначе.

Lukas Eder 11.07.2024 09:18
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
2
5
112
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Баг или нет

Вряд ли это ошибка, просто интересный дизайн: как SELECT работает как запрос верхнего уровня (аналогично стандартному SQL), а не как подзапрос. Этот пример показывает, что все подзапросы UNION могут иметь «локальные» предложения ORDER BY .. OFFSET, а завершающее предложение не является глобальным для вывода UNION:

select *
from (
  select i
  from (values (1), (2)) as t (i)
  order by i
  offset 0 rows
  fetch next 1 rows only
  union 
  select i
  from (values (3), (4)) as t (i)
  order by i
  offset 0 rows
  fetch next 1 rows only
) t;

Это производит:

|i  |
|---|
|1  |
|3  |

Итак, это намекает на то, что просто отсутствует/несогласованная документация.

Если подзапрос выполняется как запрос верхнего уровня, возникает эта ошибка:

Ошибка SQL [156] [S0001]: неправильный синтаксис рядом с ключевым словом «объединение».

Обходной путь

Просто вложите операцию установки еще раз в производную таблицу:

select *
from (
  select *
  from (
    select i
    from (values (1)) as t (i)
    union 
    select i
    from (values (2), (3)) as t (i)
  ) t
  order by i
  offset 0 rows
  fetch next 1 rows only
) t;

Теперь предложение ORDER BY .. OFFSET снова ведет себя и применяется ко всему выводу UNION, давая ожидаемый результат:

|i  |
|---|
|1  |

см.: DBFIDDLE, который я создал, но опоздал с ответом 😉

Luuk 09.07.2024 16:33

@Luuk: Спасибо за попытку. Я уже знал этот обходной путь и задокументировал его здесь для себя в будущем, который наверняка столкнется с этим вопросом снова, согласно рекомендациям: stackoverflow.blog/2011/07/01/…

Lukas Eder 09.07.2024 16:37

Stackoverflow говорит то же самое: Могу ли я ответить на свой вопрос? 😉

Luuk 09.07.2024 16:45

Документы находятся на Github, так что, по крайней мере, вы можете поднять проблему и воспользоваться ими.

Charlieface 10.07.2024 10:15

@Charlieface: Я предоставил отзыв. Я не буду отправлять пиар, потому что это кажется более стратегическим, чем простая опечатка.

Lukas Eder 10.07.2024 10:33

Нет, это ошибка SQL Server; результат вообще не имеет смысла. Запрос прекрасно работает в Oracle , DB2 , PostgreSQL и MySQL.

The Impaler 10.07.2024 15:39

@TheImpaler: Диалектам разрешено реализовывать разную семантику для разных функций языка. В противном случае поведение Oracle 'a' || NULL = 'a' тоже было бы ошибкой, но это просто другая реализация. Я мог бы привести вам сотни подобных примеров.

Lukas Eder 10.07.2024 16:40

Другие вопросы по теме

Запрос T-SQL для получения имени компьютера, который не отправлял запросы к базе данных в течение 60 дней
Использовать значение из одного запроса SQL в качестве имени столбца в другом запросе SQL
Процедура FINTAG_INSTEADOFINSERTTRIGGER, строка 41. Невозможно вставить явное значение в столбец метки времени
Ошибка «Не удалось преобразовать строку символов в уникальный идентификатор». при использовании UNION ALL
Поле идентификатора объекта JPA с последовательностью SQL Server 2022
Функция выполняется медленно (12 с) в PHP, но быстро (до секунды) в SSMS. Каковы некоторые области расследования?
Запрос на объединение данных строк в каждой группе
Поток данных Azure Synapse: невозможно использовать параметр в сценариях Pre SQL
Запретить обновление столбца на основе существующего значения
Как SQL Server допускает использование нескольких одинаковых псевдонимов в одном операторе SELECT?