如何正确使用Oracle ORDER BY和ROWNUM?


126

我很难将存储过程从SQL Server转换为Oracle,以使我们的产品与其兼容。

我有一个查询,它根据时间戳返回一些表的最新记录:

SQL Server:

SELECT TOP 1 *
FROM RACEWAY_INPUT_LABO
ORDER BY t_stamp DESC

=>那将返回我最近的记录

但是甲骨文:

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

而且有效。但这对我来说听起来像是一个骇人听闻的骇客,尤其是当我在涉及的表中有很多记录的时候。

实现此目标的最佳方法是什么?



4
您在上一个查询中所做的操作是正确的。您选择记录的有序列表的第一行。只需查询封装。
araknoid


5
@a_horse_with_no_name您的意思是清楚地记录在此404错误中。
anthonybrice 2015年

3
@anthonybrice:谢谢。Oracle将其所有URL更改为手册。向上最新的链接是:docs.oracle.com/cd/E11882_01/server.112/e41084/...
a_horse_with_no_name

Answers:


120

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”。您可能需要单独列出各列以避免这种情况。


40

使用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
ROWNUMROW_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


3
Oracle中没有限制。你在问这个问题。
philipxy

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')  

将大大减少结果。然后,您可以进行ORDER BY甚至进行外部查询来限制行。

另外,我认为TOAD具有限制行的功能。但是,不确定在Oracle的实际查询中是否存在限制。不确定。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.