Как правильно использовать Oracle ORDER BY и ROWNUM?

127

Мне трудно преобразовать хранимые процедуры из SQL Server в Oracle, чтобы наш продукт был совместим с ним.

У меня есть запросы, которые возвращают самую последнюю запись некоторых таблиц на основе метки времени:

SQL Server:

SELECT TOP 1 *
FROM RACEWAY_INPUT_LABO
ORDER BY t_stamp DESC

=> Это вернет мне самую последнюю запись

Но Oracle:

SELECT *
FROM raceway_input_labo 
WHERE  rownum <= 1
ORDER BY t_stamp DESC

=> Это вернет мне самую старую запись (вероятно, в зависимости от индекса), независимо от ORDER BYутверждения!

Я инкапсулировал запрос Oracle таким образом, чтобы он соответствовал моим требованиям:

SELECT * 
FROM 
    (SELECT *
     FROM raceway_input_labo 
     ORDER BY t_stamp DESC)
WHERE  rownum <= 1

и это работает. Но для меня это звучит как ужасный взлом, особенно если у меня много записей в задействованных таблицах.

Как лучше всего этого добиться?

Larry
источник
4
То, что вы сделали в своем последнем запросе, правильно. Вы выбираете 1-ю строку упорядоченного списка записей. Просто инкапсуляция запросов.
araknoid
1
Это четко задокументировано в руководстве: docs.oracle.com/cd/E11882_01/server.112/e26088/…
a_horse_with_no_name
5
@a_horse_with_no_name Вы имеете в виду четко задокументированную в этой ошибке 404.
anthonybrice
3
@anthonybrice: спасибо. Oracle изменила все свои URL-адреса на руководство. Текущая
a_horse_with_no_name

Ответы:

121

whereЗаявление запускается на выполнение , прежде чемorder by . Итак, ваш желаемый запрос гласит: « возьмите первую строку, а затем отсортируйте ее по t_stamp убыванию ». И это не то, что вы хотели.

Метод подзапроса - подходящий метод для этого в Oracle.

Если вам нужна версия, которая работает на обоих серверах, вы можете использовать:

select ril.*
from (select ril.*, row_number() over (order by t_stamp desc) as seqnum
      from raceway_input_labo ril
     ) ril
where seqnum = 1

Внешний *вернет "1" в последнем столбце. Чтобы избежать этого, вам нужно будет перечислить столбцы по отдельности.

Гордон Линофф
источник
41

ROW_NUMBER()Вместо этого используйте . ROWNUMэто псевдостолбец и ROW_NUMBER()функция. Вы можете прочитать о различиях между ними и увидеть разницу в выводе запросов ниже:

SELECT * FROM (SELECT rownum, deptno, ename
           FROM scott.emp
        ORDER BY deptno
       )
 WHERE rownum <= 3
 /

ROWNUM    DEPTNO    ENAME
---------------------------
 7        10    CLARK
 14       10    MILLER
 9        10    KING


 SELECT * FROM 
 (
  SELECT deptno, ename
       , ROW_NUMBER() OVER (ORDER BY deptno) rno
  FROM scott.emp
 ORDER BY deptno
 )
WHERE rno <= 3
/

DEPTNO    ENAME    RNO
-------------------------
10    CLARK        1
10    MILLER       2
10    KING         3
Искусство
источник
3
ROWNUMможет быть быстрее, чем ROW_NUMBER()так, следует ли использовать одно вместо другого, зависит от ряда факторов.
Дэвид Фабер
Приносим извинения за голос против, это было ошибкой! К сожалению, сейчас я не могу его забрать.
Атафуд
0

Альтернативный вариант, который я бы предложил в этом случае использования, - использовать MAX (t_stamp) для получения последней строки ... например

select t.* from raceway_input_labo t
where t.t_stamp = (select max(t_stamp) from raceway_input_labo) 
limit 1

Мое предпочтение шаблона кодирования (возможно) - надежное, обычно работает на уровне или лучше, чем попытка выбрать 1-ю строку из отсортированного списка - также намерение более читабельно.
Надеюсь это поможет ...

SQLer

SQLer
источник
3
В Oracle нет ПРЕДЕЛОВ. Вы задаетесь вопросом.
Филипси
0

Задокументировано несколько проблем с дизайном в комментарии выше. Вкратце, в Oracle вам нужно ограничить результаты вручную, если у вас есть большие таблицы и / или таблицы с одинаковыми именами столбцов (и вы не хотите явно вводить их все и переименовывать их все). Простое решение - определить точку останова и ограничить ее в своем запросе. Или вы также можете сделать это во внутреннем запросе, если у вас нет ограничения конфликтующих имен столбцов. Например

WHERE m_api_log.created_date BETWEEN TO_DATE('10/23/2015 05:00', 'MM/DD/YYYY HH24:MI') 
                                 AND TO_DATE('10/30/2015 23:59', 'MM/DD/YYYY HH24:MI')  

существенно сократит результаты. Затем вы можете ЗАКАЗАТЬ или даже выполнить внешний запрос для ограничения строк.

Кроме того, я думаю, что у TOAD есть функция ограничения строк; но не уверен, что это ограничение в рамках фактического запроса на Oracle. Точно сказать не могу.

Макс Вебер
источник