【问题标题】:How to use Oracle ORDER BY and ROWNUM correctly?如何正确使用 Oracle ORDER BY 和 ROWNUM?
【发布时间】:2013-02-12 01:33:58
【问题描述】:

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

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

SQL 服务器:

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

它有效。但这对我来说听起来像是一个可怕的 hack,尤其是当我在相关表中有很多记录时。

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

【问题讨论】:

标签: sql oracle sql-order-by rownum


【解决方案1】:

where 语句在order by 之前执行。因此,您想要的查询是“取第一行,然后按t_stampdesc排序”。这不是你想要的。

子查询方法是在 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
    

    【讨论】:

    • ROWNUM 可能比ROW_NUMBER() 更快,因此是否应该使用其中一个取决于许多因素。
    • 为错误的投票表示歉意!不幸的是,我现在无法收回。
    【解决方案3】:

    从 Oracle 12c 开始,我们现在有了 row limiting clauses,它正是这样做的。

    SELECT *
    FROM raceway_input_labo 
    ORDER BY t_stamp DESC
    FETCH FIRST ROW ONLY
    

    或多个alternatives 用于不同的场景(前 n 行、平局处理等)。

    【讨论】:

      【解决方案4】:

      在这个用例中我建议的替代方法是使用 MAX(t_stamp) 来获取最新的行......例如

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

      我的编码模式偏好(也许) - 可靠,通常比尝试从排序列表中选择第一行表现更好 - 而且意图更明确可读。
      希望这会有所帮助...

      SQLer

      【讨论】:

      • Oracle 没有限制。你在乞求这个问题。
      【解决方案5】:

      在上面的评论中记录了几个与此相关的设计问题。简短的故事,在 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 上的实际查询中。不确定。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-19
        • 2016-04-27
        • 2013-12-07
        • 2014-12-27
        相关资源
        最近更新 更多