【问题标题】:Best practice for pagination in Oracle?Oracle中分页的最佳实践?
【发布时间】:2012-11-24 03:52:11
【问题描述】:

问题:我需要编写将返回单页行的结果集的存储过程总行数。

解决方案 A:我创建了两个存储过程,一个返回单个页面的结果集,另一个返回标量 - 总行数。解释计划说第一个 sproc 的成本为 9,第二个的成本为 3。

SELECT  *
FROM    ( SELECT ROW_NUMBER() OVER ( ORDER BY D.ID DESC ) AS RowNum, ...
        ) AS PageResult
WHERE   RowNum >= @from
    AND RowNum < @to
ORDER BY RowNum

SELECT  COUNT(*)
FROM    ...

解决方案 B:我通过将相同的 TotalRows 数字添加到结果集中的 每一 行,将所有内容放在一个存储过程中。这个解决方案感觉很hackish,但是成本为9,并且只有一个sproc,所以我倾向于使用这个解决方案。

SELECT * 
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY D.ID DESC  ) RowNum, COUNT(*) OVER () TotalRows,
WHERE RowNum >= from
        AND RowNum < to
ORDER BY RowNum;

Oracle 中是否有分页的最佳实践?上述哪种解决方案在实践中最常用?他们中的任何一个被认为是完全错误的吗?请注意,我的数据库现在并且将保持相对较小(小于 10GB)。

我正在使用 Oracle 11g 和带有 VS2010 SP1 和 Entity Framework 4.4 的最新 ODP.NET。我需要最终解决方案才能在 EF 4.4 中工作。我确信总体上可能有更好的分页方法,但我需要它们与 EF 一起使用。

【问题讨论】:

  • EF 中的分页与数据库无关。
  • 是的,但我只是想明确一点,我不想使用 ODP.NET 或 ADO.NET 特定的代码,而是宁愿停留在高级别的地方。跨度>
  • Tom Kyte 写了一篇关于使用 rownum 限制结果集的文章:oracle.com/technetwork/issue-archive/2006/06-sep/… 它可能会回答您的一些问题。

标签: oracle pagination


【解决方案1】:

如果您已经在使用分析 (ROW_NUMBER() OVER ...),那么在同一分区上添加另一个分析函数将为查询增加可忽略不计的成本。

另一方面,还有很多其他方法可以进行分页,其中一种使用rownum

SELECT * 
  FROM (SELECT A.*, rownum rn
          FROM (SELECT *
                  FROM your_table
                 ORDER BY col) A
         WHERE rownum <= :Y)
 WHERE rn >= :X

如果您在排序列上有适当的索引,则此方法会更好。在这种情况下,使用两个查询可能更有效(一个查询总行数,一个查询结果)。

这两种方法都是合适的,但一般来说,如果您想要行数和分页集,那么使用分析会更有效,因为您只查询行一次。

【讨论】:

  • 请参阅 Oracle 杂志(2006 年 9 月/10 月)中的 Tom Kyte “On ROWNUM and Limiting Results”。
  • 我在语法 * 后跟第二行的逗号有问题。似乎不喜欢语法。你需要 A.* 代替,然后是 col 之后的 A)
  • 为什么你在两个查询而不是一个查询中过滤了rownum?可能是“WHERE rownum = :X”。它的性能是否很差?
  • @IúridosAnjos:Ludovic Kuty 建议的 Tom Kyte article 解释了为什么它不起作用:rownum &gt;= 2 总是 FALSE
  • @IúridosAnjos:内部SELECT 用于排序,不能包含ROWNUM,因为ROWNUM 是在排序之前计算的。 Here's an example with dbfiddle。当然,Oracle Magazine article 中也对此进行了解释,我再次鼓励您阅读 :)
【解决方案2】:

这可能会有所帮助:

   SELECT * FROM 
     ( SELECT deptno, ename, sal, ROW_NUMBER() OVER (ORDER BY ename) Row_Num FROM emp)
     WHERE Row_Num BETWEEN 5 and 10;

【讨论】:

  • 这正是 OP 在问题中所包含的内容......您必须解释 为什么 它会有所帮助,因为 OP 正在寻找性能最高的解决方案。为什么这比使用 rownum 更好,在什么情况下?你有任何基准吗?
  • 嗯,这就是我对问题的理解。我希望我做对了。 ROWNUM 和 ROW_NUMBER() 之间的区别(在 Oracle 中)是第一个是伪列,最后一个是解析函数。您可以同时使用两者。但是 ROWNUM 并不总是与 ORDER BY 一起使用。尝试使用 ROWNUM 添加 Order by,您将看到不同之处。并且使用分析函数是最好的和最新的,并且通常可以提高性能。您可以在文档中阅读有关 ROWNUM 和 ROW_NUMBER() 的更多信息。谢谢。
  • 是的,你是对的。当与 ORDER BY 结合使用时,rownum 谓词将返回一个随机值,除非排序是在子选择中完成的。但是,我的观点是您的回答没有回答问题。这是关于它是否会起作用的评论。在这种情况下,尽管“最近”的分析函数不一定rownum 更有效,因为它们需要对表进行额外扫描,而rownum 不需要(如文森特的回答中所述)。
【解决方案3】:

试试这个:

select * from ( select * from "table" order by "column" desc ) where ROWNUM > 0 and ROWNUM <= 5;

【讨论】:

  • 很好的答案附有代码示例,并为未来的读者提供了解释。虽然问这个问题的人可能理解你的答案,但解释你是如何得出这个问题的将帮助无数其他人。
  • hmm...如果我将 rownum > 0 更改为 rownum > 1 将不起作用。我现在不知道为什么...
【解决方案4】:

对不起,这个适用于排序:

SELECT * FROM (SELECT ROWNUM rnum,a.* FROM (SELECT * FROM "tabla" order by "column" asc) a) WHERE rnum BETWEEN "firstrange" AND "lastrange";

【讨论】:

  • 您好,欢迎来到 StackOverflow。请注意,如果您有改进,您应该编辑现有答案,而不是创建新答案。谢谢!
【解决方案5】:

我也遇到了类似的问题。我尝试了上述所有解决方案,但没有一个能给我带来更好的性能。我有一张包含数百万条记录的表格,我需要以 20 页为单位在屏幕上显示它们。我已完成以下操作来解决问题。

  1. 在表中添加新列 ROW_NUMBER。
  2. 将该列设为主键或在其上添加唯一索引。
  3. 使用填充程序(在我的例子中是 Informatica)用 rownum 填充列。
  4. 使用 between 语句从表中获取记录。 (从 LOWER_RANGE 和 UPPER_RANGE 之间的 ROW_NUMBER 表中选择 *)。

如果我们需要在一个巨大的表上进行无条件的分页获取,这个方法是有效的。

【讨论】:

    【解决方案6】:

    在 Oracle 12C 中,您可以使用限制 LIMITOFFSET 进行分页。

    示例 - 假设您有一个表tab,需要根据DATE 数据类型列dt 使用分页降序从中获取数据。

    page_size:=5
    
    select * from tab
    order by dt desc
    OFFSET nvl(page_no-1,1)*page_size ROWS FETCH NEXT page_size ROWS ONLY;
    

    解释:

    page_no=1 page_size=5

    OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY - 仅获取第一 5 行

    page_no=2 page_size=5

    OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY - 获取接下来的 5 行

    等等。

    参考页 -

    https://dba-presents.com/index.php/databases/oracle/31-new-pagination-method-in-oracle-12c-offset-fetch

    https://oracle-base.com/articles/12c/row-limiting-clause-for-top-n-queries-12cr1#paging

    【讨论】:

    • 可悲的是缺少 COUNT(*) 作为某种变量 o 内置列与给定标准的总记录。我知道这可能会产生成本,但开发人员无论如何都会这样做,以确定我们总共有多少页面。
    【解决方案7】:

    通过WITH 语句来组织SQL 代码是一种简洁的方式。

    精简版还实现了结果总数总页数

    例如

    WITH SELECTION AS (
        SELECT FIELDA, FIELDB, FIELDC FROM TABLE), 
    NUMBERED AS (
        SELECT 
        ROW_NUMBER() OVER (ORDER BY FIELDA) RN, 
        SELECTION.*
        FROM SELECTION)
    SELECT
        (SELECT COUNT(*) FROM NUMBERED) TOTAL_ROWS,
        NUMBERED.*
    FROM NUMBERED
    WHERE 
        RN BETWEEN ((:page_size*:page_number)-:page_size+1) AND (:page_size*:page_number)
    

    此代码为您提供了一个包含另外两个字段的分页结果集:

    • TOTAL_ROWS 与您的完整 SELECTION 的总行数
    • RN记录的行号

    它需要 2 个参数::page_size:page_number 来分割你的 SELECTION

    精简版

    选择已经实现ROW_NUMBER()字段

    WITH SELECTION AS (
        SELECT 
            ROW_NUMBER() OVER (ORDER BY FIELDA) RN,
            FIELDA, 
            FIELDB, 
            FIELDC 
        FROM TABLE) 
    SELECT
        :page_number PAGE_NUMBER,
        CEIL((SELECT COUNT(*) FROM SELECTION ) / :page_size) TOTAL_PAGES,
        :page_size PAGE_SIZE,
        (SELECT COUNT(*) FROM SELECTION ) TOTAL_ROWS,
        SELECTION.*
    FROM SELECTION 
    WHERE 
        RN BETWEEN ((:page_size*:page_number)-:page_size+1) AND (:page_size*:page_number)
    

    【讨论】:

      猜你喜欢
      • 2012-12-02
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 2014-06-21
      • 2023-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多