【问题标题】:Oracle ROWNUM performanceOracle ROWNUM 性能
【发布时间】:2012-02-26 00:44:40
【问题描述】:

要在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 使得大表的第一次查询效率低下。但是我无法通过查看它们来真正区分两个查询的逻辑。 谁能解释一下人类语言中两个查询之间的逻辑差异?

提前致谢!

【问题讨论】:

  • id 是绑定变量,否(应该是 :id ?)如果是,使用什么值(相同?)
  • 我认为您在第二个版本中用于过滤器的rownum 不能保证与第一个版本中的相同;认为您需要为第二个查询设置别名并引用它,或者在针对a 的查询中添加order by rownum?我怀疑这会影响速度。

标签: sql performance oracle


【解决方案1】:

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

这就是 Oracle 为第二个查询选择不同计划的原因——它在逻辑上无法将 STOPKEY 操作应用于最里面的查询块。

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

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

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

【讨论】:

  • 很好的解释!谢谢。 @Alex:似乎最好添加order by rownum,因为它在执行计划中添加了“SORT ORDER BY STOPRKEY”,而别名rownum 在执行计划中删除了“COUNT STOPKEY”。但是,正如你所说,我没有看到速度的变化。
猜你喜欢
  • 2012-09-13
  • 2011-09-30
  • 1970-01-01
  • 2017-07-05
  • 2019-03-24
  • 1970-01-01
  • 1970-01-01
  • 2014-04-05
相关资源
最近更新 更多