Как правильно использовать Oracle ORDER BY и ROWNUM?
Мне сложно преобразовать хранимые процедуры из 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
и он работает. Но это звучит как ужасный хак для меня, особенно если у меня много записей в вовлеченных таблицах.
Каков наилучший способ достичь этого?
Ответы
Ответ 1
Оператор where
выполняется перед order by
. Итак, ваш желаемый запрос говорит: "Возьмите первую строку, а затем закажите ее t_stamp
desc". И это не то, что вы намереваетесь.
Метод подзапроса - это правильный метод для этого в 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" в последнем столбце. Чтобы избежать этого, вам нужно будет перечислять столбцы отдельно.
Ответ 2
Используйте 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
Альтернативный вариант, который я предложил бы в этом случае использовать 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
Ответ 4
Документировал пару проблем с дизайном с этим в комментарии выше. Краткая история, в 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')
существенно сократит результаты. Затем вы можете ЗАКАЗАТЬ BY или даже сделать внешний запрос для ограничения строк.
Кроме того, я думаю, что TOAD имеет функцию ограничения строк; но, не уверен, что ограничивает в рамках реального запроса Oracle. Не уверен.
Ответ 5
Просто используйте rownum, как показано ниже.
select *
from (select t.*
from raceway_input_labo ril
order by t_stamp desc
)
where rownum = 1