Oracle分页:分析函数ROW_NUMBER()VS ROWNUM

时间:2011-08-16 03:16:04

标签: oracle pagination paging row-number rownum

当需要在网站上进行分页时...哪种方法表现更好?

分析功能 - ROW_NUMBER()

ROWNUM

  • http://www.oracle.com/technetwork/issue-archive/2007/07-jan/o56asktom-086197.html
  • INMHO我发现这种方法更具人性化代码

    SELECT * FROM (
      SELECT rownum rn, a.* 
      FROM(
        SELECT columnA, columnB
        FROM table 
        ORDER BY columnB
      ) a 
      WHERE rn <= OFFSET
    )
    WHERE rnum >= LOW_LIMIT
    
    • 注意:我知道有RANK和DENSE_RANK分析函数,但我们假设我只需要通过确定性查询页面。

    • 注意2:使用单独的简单查询计数检索我正在考虑的记录总数(*)

3 个答案:

答案 0 :(得分:7)

我认为这个问题很有趣,所以我尝试了一些东西。

我有一个名为large_t的表,它包含大约1.1M行。

然后我有两个问题:

select * 
from
(
  select rownum rnum, a.*
  from (
         select owner, object_name, object_id
         from large_t
         order by object_id
       ) a
  where rownum   <= 30      
) where rnum > 20;

select *
from
(
select owner, object_name, object_id,
       row_number() over (order by object_id) rnum
from large_t
) where rnum > 20 and rnum <= 30;

如果你查看两个查询生成的计划,第一个有一个操作:

SORT ORDER BY STOPKEY

分析查询包含名为

的操作
WINDOW SORT PUSHED RANK

SORT ORDER BY STOPKEY是一种更有效的排序操作,它是一个简单的ORDER BY。我不确定WINDOW SORT PUSHED RANK是如何工作的,但它似乎以类似的方式工作。

在运行两个查询后查看v $ sql_workarea,两者都只需要一个4096字节的sort_area。

相反,如果我在没有分页查询的情况下运行查询:

select owner, object_name, object_id
from large_t
order by object_id

然后所需的排序区域为37M,证明两个查询中的排序大致相同。

通常,如果您想要有效地返回已排序查询的TOP N,您将需要排序列上的索引 - 这将阻止Oracle根本需要排序。所以,我在OBJECT_ID上创建了一个索引,然后再次解释了这两个查询。

这次第一个查询使用索引并在0.2秒内返回,而第二个查询没有使用新索引并且速度慢得多。

因此,我从这一快速分析的结论是,在使用rownum过滤或分析row_number函数的一般情况下,两者的执行大致相同。但是,当row_number没有时,rownum示例使用我在表上创建的索引自动启动。也许我可以通过一些提示来使用索引 - 这是你可以尝试的其他东西。

答案 1 :(得分:4)

除了答案中提到的其他差异外,您还应该考虑表现。这里有一个非权威但非常有趣的报告,比较各种分页方式,其中使用ROWNUMROW_NUMBER() OVER()进行比较:

https://web.archive.org/web/20160901191310/http://www.inf.unideb.hu:80/~gabora/pagination/results.html

答案 2 :(得分:0)

生成自己的实证结果:

-- Create test table
CREATE TABLE test_large_tab (
  tlt_id   NUMBER,
  tlt_data VARCHAR2(50)
);

-- Load with data
BEGIN
   FORALL i IN 1 .. 1000000
      INSERT INTO test_large_tab
      (
       tlt_id,
       tlt_data
      )
      VALUES
      (
       i,
       TO_CHAR(sysdate-i, 'FMMon ddth, YYYY')
      );
END;

当然,您可以增加表格的大小以适合您的测试目的!

设置计时并对大表运行两个查询。

更改表格结构以更好地适合您的测试,因为您可能希望为查询索引某些列等等,但实质上它只是一个简单的测试,并且不会花费很长时间来运行。

如果两者的时间大致相同,那么使用最易读(也就是可支持的)版本。