Oracle:如何只返回部分结果?

Posted

技术标签:

【中文标题】Oracle:如何只返回部分结果?【英文标题】:Oracle: How to return a partial result only? 【发布时间】:2009-03-03 16:15:50 【问题描述】:

我正在使用 Oracle 数据库。

在我的查询中提取了 100 行。如果我想过滤 rownum 50 到 60 之间的行,查询会是什么?

SELECT EMPLID, EFFDT, ACTION, ACTION_REASON 
from JOB where emplid ='12345'

【问题讨论】:

【参考方案1】:

大多数人通常会告诉您使用 ROWNUM 来执行此操作,但更简洁的方法是使用 row_number() 分析函数。

select EMPLID, EFFDT, ACTION, ACTION_REASON
from
(
SELECT EMPLID, EFFDT, ACTION, ACTION_REASON, row_number() over (order by emplid) rn
from JOB where emplid ='12345'
)
where rn between 50 and 60;

使用 row_number 函数允许您在单个查询中对结果进行排序和编号,然后您只需要一个包装查询即可获得所需的行。

【讨论】:

不正确:仅仅因为它看起来像查询中的查询中的查询,并不意味着 Oracle 会那样实现它。我只是对每种方法进行了 10,000 次测试,它们运行时间分别为 69 和 78 毫秒(你的速度较慢,但​​差别不大)。解释计划显示发生了什么 在我的系统上,时间都在我的桌子上的 1.5 毫秒范围内运行,但是 row_number 的语法更清晰,不需要两个内联视图。见 Tom Kyte,oracle.com/technology/oramag/oracle/07-jan/o17asktom.html 同意 - 你的断言是我打电话给你的“更有效”的方式!它们在所有意图和目的上都同样有效。 事实上,如果你从你的答案中删除“更有效”的说法,我会投赞成票! 我希望 rownum 方法可能更有效,因为优化器更有可能认识到返回有序行的基于索引的访问方法比获取所有行更有效计算 ROW_NUMBER。【参考方案2】:

由于我比较了 Chad's 和 Nick's 方法来对尼克的回答发表评论,我想我会在这里发布我的发现。我使用 Tom Kyte 的 runstats 包将它们与此脚本进行比较:

begin
   runstats_pkg.rs_start('Chad');
   for i in 1..10000 loop
     for r in (
      SELECT EMPLID,EFFDT,ACTION,ACTION_REASON
      FROM (SELECT ROWNUM rnum, EMPLID,EFFDT,ACTION,ACTION_REASON
            FROM (SELECT EMPLID,EFFDT,ACTION,ACTION_REASON
                  FROM JOB
                  WHERE emplid = '12345')
            WHERE rownum <= 60
      )
      WHERE rnum >= 50
      ) loop
        null;
      end loop;
   end loop;
   runstats_pkg.rs_middle('Nick');
   for i in 1..10000 loop
     for r in (
      select EMPLID, EFFDT, ACTION, ACTION_REASON
      from
      (
      SELECT EMPLID, EFFDT, ACTION, ACTION_REASON, row_number() over (order by emplid) rn
      from JOB where emplid ='12345'
      )
      where rn between 50 and 60
      ) loop
        null;
      end loop;
    end loop;
   runstats_pkg.rs_stop(0,false,false,false,false,false,false,false,false);
end;
/

结果:

Run1 = Chad
Run2 = Nick

*** Comparative Time Report ***
Run                                   Time (hsecs)
--------------------------------------------------
Run1                                            69
Run2                                            77

Run1 ran in 89.61% of the time of Run2
Run2 ran in 111.59% of the time of Run1

PL/SQL procedure successfully completed.

使用自动跟踪可以看出计划非常相似:

SQL>           SELECT EMPLID,EFFDT,ACTION,ACTION_REASON
  2            FROM (SELECT ROWNUM rnum, EMPLID,EFFDT,ACTION,ACTION_REASON
  3                  FROM (SELECT EMPLID,EFFDT,ACTION,ACTION_REASON
  4                        FROM JOB
  5                        WHERE emplid = '12345')
  6                  WHERE rownum <= 60
  7            )
  8            WHERE rnum >= 50
  9  /

no rows selected


Execution Plan
----------------------------------------------------------

------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)|
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     1 |    41 |     3   (0)|
|*  1 |  VIEW                         |         |     1 |    41 |     3   (0)|
|*  2 |   COUNT STOPKEY               |         |       |       |            |
|   3 |    TABLE ACCESS BY INDEX ROWID| JOB     |     1 |    13 |     3   (0)|
|*  4 |     INDEX RANGE SCAN          | JOB2_PK |     1 |       |     2   (0)|
------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RNUM">=50)
   2 - filter(ROWNUM<=60)
   4 - access("EMPLID"=12345)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        264  bytes sent via SQL*Net to client
        231  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

SQL>           select EMPLID, EFFDT, ACTION, ACTION_REASON
  2            from
  3            (
  4            SELECT EMPLID, EFFDT, ACTION, ACTION_REASON, row_number() over (order by emplid) rn
  5            from JOB where emplid ='12345'
  6            )
  7            where rn between 50 and 60
  8  /

no rows selected


Execution Plan
----------------------------------------------------------

------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)|
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     1 |    41 |     3   (0)|
|*  1 |  VIEW                         |         |     1 |    41 |     3   (0)|
|*  2 |   WINDOW NOSORT STOPKEY       |         |     1 |    17 |     3   (0)|
|   3 |    TABLE ACCESS BY INDEX ROWID| JOB     |     1 |    17 |     3   (0)|
|*  4 |     INDEX RANGE SCAN          | JOB2_PK |     1 |       |     2   (0)|
------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RN">=50 AND "RN"<=60)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY "EMPLID")<=60)
   4 - access("EMPLID"=12345)
       filter("EMPLID"=12345)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        264  bytes sent via SQL*Net to client
        231  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

看起来这两种方法之间没有太多选择,尽管 Chad 在我的数据库上始终稍快一些,即:

Oracle 数据库 10g 版本 10.2.0.3.0 - 64 位生产

【讨论】:

嗯。如果那不是超越,那我不知道是什么。 ;-) +1 是的,这是对我下午的悲哀控诉!【参考方案3】:

这在 Oracle 中有点棘手,我认为你必须这样做:

SELECT EMPLID,EFFDT,ACTION,ACTION_REASON
FROM (SELECT ROWNUM rnum, EMPLID,EFFDT,ACTION,ACTION_REASON
      FROM (SELECT EMPLID,EFFDT,ACTION,ACTION_REASON
            FROM JOB
            WHERE emplid = '12345')
      WHERE rownum <= 60
)
WHERE rnum >= 50;

【讨论】:

rownum ORDER BY 敏感吗? 我相信download.oracle.com/docs/cd/B28359_01/server.111/b28286/… 是的,所以在最里面的查询中使用 ORDER BY 是一个非常好的主意,他只是在他的示例中没有,所以我不想猜测正确的顺序。跨度> 感谢大家的及时回复。非常感谢您的帮助。 鲍比,你能把其中一个标记为你使用的答案吗?【参考方案4】:

您可以使用 ROWNUM(请参阅 this link)。 如果您需要控制排序顺序,可以将另一个查询包装在您的查询周围。

【讨论】:

感谢大家的及时回复。非常感谢您的帮助。

以上是关于Oracle:如何只返回部分结果?的主要内容,如果未能解决你的问题,请参考以下文章

如何选择包含特定子字符串的单词列表作为 SQL 查询(oracle)的一部分?

如何能实现将oracle的查询结果排序后,只返回第一条记录

为啥不使用存储库返回部分域模型结果 [关闭]

Oracle 学习笔记 14 -- 集合操作和高级子查询

调用oracle存储过程使用ResultSet的last方法 报错:出现对只转发结果集的无效操作: last

Oracle笔记 集合序列