Oracle中分页查询的速度

Luk*_*der 10 sql performance oracle11g rownum window-functions

这对我来说是一个永无止境的话题,我想知道我是否会忽视某些事情.基本上我在应用程序中使用两种类型的SQL语句:

  1. 具有"后备"限制的常规查询
  2. 排序和分页查询

现在,我们正在谈论针对具有数百万条记录的表的一些查询,加入另外5个具有数百万条记录的表.显然,我们几乎不想获取所有这些,这就是为什么我们有上述两种方法来限制用户查询.

案例1非常简单.我们只添加一个额外的ROWNUM过滤器:

WHERE ...
  AND ROWNUM < ?
Run Code Online (Sandbox Code Playgroud)

这是非常快的,因为Oracle的CBO将考虑其执行计划考虑此过滤器,并可能应用一个FIRST_ROWS操作(类似于/*+FIRST_ROWS*/提示强制执行的操作).

然而,案例2对Oracle来说有点棘手,因为LIMIT ... OFFSET其他RDBMS中没有任何条款.因此,我们将"业务"查询嵌套在技术包装器中:

SELECT outer.* FROM (
  SELECT * FROM (
    SELECT inner.*, ROWNUM as RNUM, MAX(ROWNUM) OVER(PARTITION BY 1) as TOTAL_ROWS
    FROM (
      [... USER SORTED business query ...]
    ) inner
  ) 
  WHERE ROWNUM < ?
) outer
WHERE outer.RNUM > ?
Run Code Online (Sandbox Code Playgroud)

请注意,TOTAL_ROWS即使不获取所有数据,也会计算该字段以了解我们将拥有多少页.现在这个分页查询通常非常令人满意.但是时不时(正如我所说,在查询5M +记录时,可能包括非索引搜索),这运行2-3分钟.

编辑:请注意,潜在的瓶颈并不容易规避,因为在分页之前必须应用排序!

我想知道,这是最先进的模拟LIMIT ... OFFSET,包括TOTAL_ROWS在Oracle中,还是有更好的解决方案,设计会更快,例如使用ROW_NUMBER()窗口函数而不是ROWNUM伪列?

Ton*_*ews 6

情况2的主要问题是,在许多情况下,必须获取整个查询结果集,然后返回前N行之前对其进行排序- 除非对ORDER BY列进行索引,Oracle可以使用索引来避免排序.对于复杂查询和大量数据,这可能需要一些时间.但是,您可以采取一些措施来提高速度:

  1. 尝试确保在内部SQL中不调用任何函数 - 这些函数可能会被调用500万次,只返回前20行.如果您可以将这些函数调用移动到外部查询,则会调用它们.
  2. 使用FIRST_ROWS_n提示来推动Oracle优化,因为您永远不会返回所有数据.

编辑:

另一个想法是:您当前正在向用户呈现可能返回数千或数百万行的报告,但用户实际上从未真实地翻阅所有行.您是否可以强迫他们选择较少量的数据,例如将所选日期范围限制为3个月(或其他)?