Вычисление SQL Server ROW_NUMBER() OVER() для производной таблицы

В некоторых других базах данных (например, DB2 или Oracle с ROWNUM) я могу опустить предложение ORDER BY в предложении OVER() функции ранжирования. Например:

ROW_NUMBER() OVER()

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

SELECT t.*, ROW_NUMBER() OVER()
FROM (
    SELECT ...
    ORDER BY
) t

Как это можно эмулировать в SQL Server? Я нашел людей, использующих это трюк, но это неправильно, так как он будет вести себя недетерминированно по отношению к порядку из производной таблицы:

-- This order here ---------------------vvvvvvvv
SELECT t.*, ROW_NUMBER() OVER(ORDER BY (SELECT 1))
FROM (
    SELECT TOP 100 PERCENT ...
    -- vvvvv ----redefines this order here
    ORDER BY
) t

Конкретный пример (как можно увидеть на SQLFiddle):

SELECT v, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM (
  SELECT TOP 100 PERCENT 1 UNION ALL
  SELECT TOP 100 PERCENT 2 UNION ALL
  SELECT TOP 100 PERCENT 3 UNION ALL
  SELECT TOP 100 PERCENT 4
  -- This descending order is not maintained in the outer query
  ORDER BY 1 DESC
) t(v)

Кроме того, я не могу повторно использовать какое-либо выражение из производной таблицы для воспроизведения предложения ORDER BY в моем случае, поскольку производная таблица может быть недоступна, поскольку она может быть предоставлена ​​какой-то внешней логикой.

Итак, как я могу это сделать? Могу ли я это сделать вообще?


person Lukas Eder    schedule 23.09.2013    source источник
comment
как насчет SELECT NULL? это все равно даст неверный результат?   -  person John Woo    schedule 23.09.2013
comment
@491243: Да. Что ж, результат, очевидно, верен, но мне интересно, действительно ли поведение пустого OVER() четко определено, или это работает по совпадению в DB2... Я подготовлю SQL Fiddle, чтобы проиллюстрировать это.   -  person Lukas Eder    schedule 23.09.2013
comment
Вам нужно поместить row_number во внутренний запрос, поэтому, если вы не можете изменить это, я думаю, вам не повезло.   -  person Laurence    schedule 23.09.2013
comment
@Laurence: Есть две проблемы. 1) у меня не обязательно есть доступ к внутреннему запросу, 2) внутренний запрос может содержать DISTINCT, в случае чего добавление ROW_NUMBER() изменит семантику внутреннего запроса.   -  person Lukas Eder    schedule 23.09.2013
comment
в некоторых случаях это работает: sqlfiddle.com/#!6/167e3/24 кажется как часть top 100% является проблемой? (как в top 10000000000 работает), хотя, если вы не можете изменить внутренний запрос, можете ли вы добавить ограничение сверху?   -  person    schedule 23.09.2013
comment
@gordatron: Интересно. Обратите внимание: я не изменяю внутренний запрос, добавляя top 100%, top N. Я просто хочу быть готовым к любому внутреннему запросу, который я получаю, например. когда у него есть пункт TOP .. ORDER BY...   -  person Lukas Eder    schedule 23.09.2013
comment
@LukasEder Я не эксперт, но не думаю, что порядок по действителен даже для внутреннего запроса без вершины или подобного, чтобы придать ему значение ... другой конец той же логики, которая настаивает на том, чтобы вы ранжировали;)   -  person    schedule 24.09.2013
comment
Что ж, я тоже думаю. Но я хотел бы знать ;-)   -  person Lukas Eder    schedule 24.09.2013
comment
Следует отметить, что в вашем конкретном примере кода неоднозначно, применяется ли ORDER BY только к последнему запросу TOP или если он заказывает весь набор. Попробуйте изменить порядок строк, которые UNIONed вместе, и посмотрите, получится ли у вас тот же результат?   -  person ErikE    schedule 25.09.2013
comment
@ErikE: во всех известных мне диалектах SQL ORDER BY применяется к результату всех объединенных подзапросов. В SQL Server есть <SELECT statement> ::= <query_expression> [ ORDER BY ], как видно здесь: technet.microsoft.com /de-de/library/ms189499.aspx. Так что я не думаю, что есть какая-то двусмысленность   -  person Lukas Eder    schedule 25.09.2013
comment
@LukasEder Если вы изучите эту скрипту SQL, я думаю, вы увидите некоторые неожиданные результаты. В вашем запросе как есть ORDER BY применяется только к четвертому SELECT во внутреннем запросе. Но когда этот запрос переносится на верхний уровень, ORDER BY работает для ОБОИХ значений: выбор конкретных строк в финальном SELECT а также упорядочение всего набора результатов. Мое чутье насчет возможной двусмысленности не было ошибочным.   -  person ErikE    schedule 26.09.2013
comment
@ErikE: я все еще не думаю, что в объединении UNION и ORDER BY есть какая-то двусмысленность :-) Внешний запрос в вашем первом примере просто не содержит предложения ORDER BY, и SQL-сервер не дает вам никаких гарантий в отношении порядка, тогда...   -  person Lukas Eder    schedule 27.09.2013
comment
@LukasEder Вы должны были заметить, что внутренний ORDER BY выбрал 7 и 6 для последних UNION ALL, доказывая, что ORDER BY применялся только к последним SELECT. Во втором запросе он делал то же самое: выбирал только 7 и 6 и также упорядочивал окончательный результат. Таким образом, последняя ORDER BY в серии UNIONs может функционировать по-разному, поэтому я считаю, что двусмысленность все еще присутствует. Примечание. У меня была опечатка в скрипке, которая не изменила набор результатов, но слегка испортила его, поэтому вот вам обновленный SQL Fiddle.   -  person ErikE    schedule 27.09.2013


Ответы (1)


Уловку Row_Number() OVER (ORDER BY (SELECT 1)) следует НЕ рассматривать как способ избежать изменения порядка базовых данных. Это всего лишь средство избежать выполнения сервером дополнительной и ненужной сортировки (он все еще может выполнять сортировку, но это будет стоить минимально возможной суммы по сравнению с сортировкой по столбцу).

Все запросы в SQL-сервере АБСОЛЮТНО ДОЛЖНЫ иметь предложение ORDER BY в самом внешнем запросе, чтобы результаты были надежно упорядочены гарантированным образом.

Понятие «сохранение исходного порядка» не существует в реляционных базах данных. Таблицы и запросы всегда должны считаться неупорядоченными до тех пор, пока в самом внешнем запросе не будет указано предложение ORDER BY.

Вы можете попробовать один и тот же неупорядоченный запрос 100 000 раз и всегда получать его с одним и тем же порядком, и, таким образом, прийти к выводу, что можете положиться на указанный порядок. Но это было бы ошибкой, потому что однажды что-то изменится, и в нем не будет того порядка, которого вы ожидаете. Одним из примеров является обновление базы данных до новой версии SQL Server — это привело к тому, что многие запросы изменили свой порядок. Но это не обязательно должно быть такое большое изменение. Такая мелочь, как добавление или удаление индекса, может вызвать различия. И еще: Установка пакета обновлений. Разделение таблицы. Создание индексированного представления, включающего рассматриваемую таблицу. Достижение некоторого переломного момента, когда вместо поиска выбирается сканирование. И так далее.

Не полагайтесь на результаты, которые будут упорядочены, если вы не сказали «Сервер, ORDER BY».

person ErikE    schedule 23.09.2013
comment
Я не совсем согласен с вашей оценкой. Возьмем, к примеру, Oracle ROWNUM. Это довольно волшебно, поскольку гарантирует создание фактического номера строки для каждой строки. Это делает его очень нереляционным псевдостолбцом, поскольку к нему можно получить доступ в семантически сложных ситуациях, например. пункт WHERE. Похожая странная функция Oracle — FOR UPDATE SKIP LOCKED, которая инвертирует семантику предложения SQL. Но SQL в любом случае не является реляционным на 100%, поэтому я подумал, что может существовать аналогичный надежный способ доступа к конкретному материализованному порядку кортежей данной ссылки на таблицу в SQL Server. - person Lukas Eder; 24.09.2013
comment
К сожалению, Лукас, я не знаю способа получить первоначальный порядок за столом. Замечательно, что Oracle предоставляет некоторую полезную функциональность, так что, возможно, я преувеличил значение в отношении всех реляционных баз данных. Тем не менее, я верю, что правильно понял для SQL Server (и, конечно, готов к исправлению, если это необходимо). - person ErikE; 24.09.2013
comment
Если вам нужен исходный порядок таблицы, используйте столбец в таблице, который определяет порядок, а затем упорядочивайте по нему. В SQL Server, если вы упорядочиваете по кластеризованному индексу или по некластеризованному индексу и выбираете только столбцы в этом индексе, операция сортировки выполняться не будет. Таким образом, это то же самое, что иметь естественный порядок (т. Е. Без дополнительной обработки). - person siride; 24.09.2013
comment
Эрик, да, я боюсь, что вы правильно поняли SQL Server :-) Я заметил, как строго он навязывает четкую семантику таким функциям SQL, что, вероятно, в основном хорошо. @siride: я думаю, вы, возможно, не совсем поняли мой вопрос. - person Lukas Eder; 24.09.2013