2012-02-03 99 views
7

要在Oracle中查询top-n行,通常使用ROWNUM。 所以下面的查询似乎确定(获取最新的5个金):Oracle ROWNUM性能

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 

显然,索引全扫描DESCENDING,使低效的大表第一个查询。但我无法通过查看它们来区分两个查询的逻辑。 任何人都可以解释两种人类语言查询之间的逻辑差异吗?

在此先感谢!

+2

id是绑定变量,否(应该是:id?)如果是这样,使用了什么值(相同?) – tbone 2012-02-03 12:30:05

+2

我不认为你在第二个版本中用于过滤器的'rownum'是保证和第一个一样;认为你需要别名你的第二个查询并引用它,或者在查询中添加'order by rownum'来反对'a'?我怀疑这会影响速度。 – 2012-02-03 13:12:22

回答

3

首先,如Alex的评论所述,我不确定您的第二个版本是否100%保证给您正确的行 - 因为查询的“中间”块没有明确的order by ,Oracle没有义务将行按任何特定顺序传递给外部查询块。但是,似乎并没有什么特别的理由可以改变行从最里面的块传递的顺序,所以实际上它可能会起作用。

这就是为什么Oracle为第二个查询选择不同的计划 - 逻辑上不能将STOPKEY操作应用于最内层的查询块。

我认为在第一种情况下,优化器假设id值分布良好,并且对于任何给定的值,可能会有一些非常近期的事务。由于它可以看到它只需要查找最近5次匹配,因此它计算出使用索引从paydate降序扫描行看起来效率更高,从表中查找相应的id和其他数据,以及当前5场比赛中发现时停止。我怀疑你会发现这个查询的性能非常不同,具体取决于你使用的具体的id值 - 如果这个id有很多最近的活动,应该很快找到这些行,但是如果它没有,那么索引扫描可能需要做更多的工作。

在第二种情况下,我认为它不能将STOPKEY优化应用到最内层的块,这是由于嵌套的额外层。在这种情况下,索引全扫描将变得不太吸引人,因为它总是需要扫描整个索引。因此,它选择在id(我假设)上进行索引查找,然后在日期上进行实际排序。如果给定的id值与一小部分行相匹配,则这可能会更有效 - 但如果您给出在整个表中散布很多行的id,则我预计它会变慢,因为它将具有访问和排序许多行。

所以,我猜测你的测试已经使用了id这个数值相对较少的行,而这些行并不是最近的。如果这将是一个典型的用例,那么第二个查询对你来说可能更好(再一次,我不确定它在技术上保证产生正确的结果集)。但是如果典型值更可能有许多匹配的行和/或更可能有5个最近的行,那么第一个查询和计划可能会更好。

+0

很好的解释!谢谢。 @Alex:似乎最好在exec计划中添加“by rownum”命令,因为它在exec计划中添加了“SORT ORDER BY STOPRKEY”,而别名“rownum”则删除了exec计划中的“COUNT STOPKEY”。但是,正如你所说,我没有看到速度的变化。 – Bazi 2012-02-04 05:21:57