Oracle Paging:解析函数 ROW_NUMBER() VS ROWNUM

发布于 2024-11-29 21:41:49 字数 1076 浏览 0 评论 0原文

当网站需要进行分页时...哪种方法效果更好?

分析函数 - ROW_NUMBER()

ROWNUM

  • http: //www.oracle.com/technetwork/issue-archive/2007/07-jan/o56asktom-086197.html
  • INMHO 我发现这种方法是一种更易于人类阅读的代码

    选择 * FROM (
      SELECT rownum rn, a.* 
      从(
        选择 A 列、B 列
        从表 
        按 B 列排序
      ) 一个 
      其中 rn <= 偏移量
    )
    WHERE rnum >= LOW_LIMIT
    
    • 注意:我知道存在 RANK 和 DENSE_RANK 分析函数,但假设我只需要通过确定性查询进行分页。

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

When Paging needs to be done in an web site... Which method performs better?

Analytic function - ROW_NUMBER()

ROWNUM

  • http://www.oracle.com/technetwork/issue-archive/2007/07-jan/o56asktom-086197.html
  • INMHO I find this approach a more human-readable code

    SELECT * FROM (
      SELECT rownum rn, a.* 
      FROM(
        SELECT columnA, columnB
        FROM table 
        ORDER BY columnB
      ) a 
      WHERE rn <= OFFSET
    )
    WHERE rnum >= LOW_LIMIT
    
    • Note: I understand that there are RANK and DENSE_RANK analytic functions, but lets assume I just need page through deterministic queries.

    • Note 2: To retrieve the total amount of records I am thinking in using a separate simple query count(*)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夏有森光若流苏 2024-12-06 21:41:49

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

我有一个名为 Large_t 的表,其中包含大约 110 万行。

然后我有两个查询:

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 更有效的排序操作。我不确定窗口排序推送排名是如何工作的,但它似乎以类似的方式工作。

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

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

select owner, object_name, object_id
from large_t
order by object_id

那么所需的排序区域是 37M,证明两个查询中的排序大致相同。

通常,如果您想有效地返回排序查询的前 N ​​个,您将需要在排序列上建立索引 - 这将阻止 Oracle 完全需要排序。因此,我在 OBJECT_ID 上创建了一个索引,然后再次解释了这两个查询。

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

因此,我从快速分析中得出的结论是,在一般情况下,使用 rownum 进行过滤或分析 row_number 函数的执行效果大致相同。但是,rownum 示例自动开始使用我在表上创建的索引,而 row_number 没有。也许我可以让它使用带有一些提示的索引——这是你可以尝试的其他东西。

I thought this question was interesting, so I tried a few things out.

I have table called large_t, that contains about 1.1M rows.

Then I have two queries:

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;

And

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;

If you look at the plans the two queries generate, the first has an operation:

SORT ORDER BY STOPKEY

While the analytic query contains an operation called

WINDOW SORT PUSHED RANK

The SORT ORDER BY STOPKEY is a more efficient sort operation that a plain ORDER BY. I am not sure how a WINDOW SORT PUSHED RANK works, but it seems to work in a similar fashion.

Looking at v$sql_workarea after running both queries, both only required a sort_area of 4096 bytes.

In contrast, if I ran the query without a paging query:

select owner, object_name, object_id
from large_t
order by object_id

Then the sort area required is 37M, proving the sort in both queries is about the same.

Normally, if you want to efficiently return the TOP N of a sorted query, you will want an index on the sorting column - that will prevent Oracle needing to sort at all. So, I created an index on OBJECT_ID, and then explained both queries again.

This time the first query used the index and returned in 0.2 seconds, while the second query didn't use the new index and was much slower.

So my conclusion from this quick bit of analysis is that in the general case using rownum to filter or the analytic row_number function both perform about the same. However, the rownum example automatically started using the index I created on the table when row_number did not. Maybe I could get it to use the index with some hints - that is something else you can experiment with.

十六岁半 2024-12-06 21:41:49

除了答案中提到的其他差异之外,您还应该考虑性能。这里有一篇非权威但很有趣的报告,比较了各种分页方式,其中ROWNUM的使用与ROW_NUMBER() OVER()相比:

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

Apart from the other differences mentioned in answers, you should also consider performance. There is a non-authoritative but very interesting report here, comparing various means of pagination, among which the use of ROWNUM compared to ROW_NUMBER() OVER():

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

终难遇 2024-12-06 21:41:49

生成您自己的经验结果:

-- 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;

当然,您可以增加表的大小以满足您的测试目的!

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

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

如果两者的时间大致相同,则使用最具可读性(因此最受支持)的版本。

To generate your own empirical results:

-- 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;

Of course, you can increase the size of the table to suit your testing purposes!

Set timing on and run both queries against the large table.

Change the table structure to better suit your test as you may want some columns to be indexed for your query too etc. but essentially it is a simple test and won't take you long to run.

If both come out roughly the same timings then use the most readable (and hence supportable) version.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文