Oracle中的分页最佳实践?

时间:2012-12-06 06:33:34

标签: oracle pagination

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

解决方案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:我将所有内容都放在一个sproc中,方法是在结果集中的每个行中添加相同的TotalRows个数字。这个解决方案感觉很乱,但成本只有9而且只有一个,所以我倾向于使用这个解决方案。

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和最新的ODP.NET与VS2010 SP1和Entity Framework 4.4。我需要最终的解决方案才能在EF 4.4中运行。我相信一般来说可能有更好的分页方法,但是我需要它们与EF合作。

7 个答案:

答案 0 :(得分:25)

如果您已经在使用分析(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

如果您在订购列上有适当的索引,那么此方法将更胜一筹。在这种情况下,使用两个查询可能更有效(一个用于总行数,一个用于结果)。

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

答案 1 :(得分:5)

这可能有所帮助:

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

答案 2 :(得分:1)

在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

答案 3 :(得分:0)

试试这个:

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

答案 4 :(得分:0)

抱歉,这个适用于排序:

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

答案 5 :(得分:0)

我也面临类似的问题。我尝试了以上所有解决方案,没有人给我更好的表现。我有一张包含数百万条记录的表格,我需要在20页的屏幕上显示它们。我已经完成了下面的工作来解决这个问题。

  1. 在表格中添加新列ROW_NUMBER。
  2. 将列作为主键或在其上添加唯一索引。
  3. 使用人口计划(在我的案例中为Informatica),用rownum填充列。
  4. 使用between语句从表中获取记录。 (SELECT * FROM TABLE WHERE ROW_NUMBER在LOWER_RANGE和UPPER_RANGE之间)。
  5. 如果我们需要在巨大的表上进行无条件的分页提取,这种方法很有效。

答案 6 :(得分:0)

使用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)