Oracle ROWNUM性能

时间:2012-02-03 11:30:56

标签: sql performance oracle

要在Oracle中查询 top-n 行,通常使用ROWNUM。 所以以下查询似乎没问题(获得最近的5次付款):

select a.paydate, a.amount
from (
  select t.paydate, t.amount
  from payments t
  where t.some_id = id
  order by t.paydate desc
) a
where rownum <= 5;

但对于非常大的桌子来说,效率很低 - 对我来说它运行约10分钟。 所以我尝试了其他查询,最后我得到了一个运行时间不到一秒的查询:

select *
from (
  select  a.*, rownum
  from (select t.paydate, t.amount
        from payments t
        where t.some_id = id
        order by t.paydate desc) a
)
where rownum <= 5;

为了了解发生了什么,我查看了每个查询的执行计划。对于第一个查询:

SELECT STATEMENT, GOAL = ALL_ROWS   7   5   175
COUNT STOPKEY           
VIEW    7   5   175
TABLE ACCESS BY INDEX ROWID 7   316576866   6331537320
INDEX FULL SCAN DESCENDING  4   6   

第二名:

SELECT STATEMENT, GOAL = ALL_ROWS   86  5   175
COUNT STOPKEY           
VIEW    86  81  2835
COUNT           
VIEW    86  81  1782
SORT ORDER BY   86  81  1620
TABLE ACCESS BY INDEX ROWID 85  81  1620
INDEX RANGE SCAN    4   81  

显然, INDEX FULL SCAN DESCENDING 会使大表的第一个查询效率低下。但我无法通过查看它们来区分两个查询的逻辑。 谁能解释一下人类语言中两个查询之间的逻辑差异?

提前致谢!

1 个答案:

答案 0 :(得分:3)

首先,正如Alex的评论中提到的,我不确定你的第二个版本是否100%保证给你正确的行 - 因为查询的“中间”块没有明确的{{ 1}},Oracle没有义务按任何特定顺序将行传递到外部查询块。但是,似乎没有任何特殊原因会改变行从最里面的块传递的顺序,因此在实践中它可能会起作用。

这就是为什么Oracle为第二个查询选择不同的计划 - 它在逻辑上无法将order by操作应用于最里面的查询块。

我认为在第一种情况下,优化器假设STOPKEY值分布均匀,并且对于任何给定值,可能会有一些非常近期的事务。因为它可以看到它只需要查找最近的5个匹配项,所以它计算出使用索引以id的降序扫描行似乎更有效,查找相应的id和其他数据来自桌子,并在发现前5场比赛时停止。我怀疑你会看到这个查询的性能差别很大,具体取决于你使用的具体id值 - 如果id有很多最近的活动,那么应该很快找到行,但如果没有,则索引扫描可能需要做更多的工作。

在第二种情况下,我认为由于额外的嵌套层,它无法将paydate优化应用于最里面的块。在这种情况下,索引全扫描将变得不那么有吸引力,因为它总是需要扫描整个索引。因此,它选择在STOPKEY(我假设)上进行索引查找,然后在日期进行实际排序。如果给定的id值与一小部分行匹配,这可能会更有效 - 但是如果你给一个id遍布整个表的行有很多行,我希望它变慢,因为它必须访问和排序许多行。

所以,我猜你的测试使用了id个值相对较少但不是很近的行。如果这是一个典型的用例,那么第二个查询可能对你更好(同样,需要注意的是我不确定它在技术上是否能保证产生正确的结果集)。但是,如果典型值更可能有许多匹配的行和/或更可能有5个最近的行,那么第一个查询和计划可能会更好。