使用Oracle分页


97

我不像我想的那样熟悉Oracle。我有大约25万条记录,我希望每页显示100条。当前,我有一个存储过程,该过程使用数据适配器和数据集以及对存储过程结果的dataadapter.Fill(dataset)方法将一百万个记录的全部四分之一检索到数据集中。如果我可以将“页码”和“每页记录数”作为整数值,可以将其作为参数传递,那么最好的方法就是只获取该特定部分。假设,如果我将10作为页数传递,将120作为页数传递,则从select语句中获得的数值将是第1880至1200,或者类似的数字,我的脑海中可能已经关闭。

我正在使用C#在.NET中进行此操作,认为这并不重要,如果我可以在sql端正确使用它,那么我应该很酷。

更新:我能够使用Brian的建议,而且效果很好。我想进行一些优化,但是页面将​​在4到5秒而不是一分钟的时间内显示出来,并且我的分页控件能够很好地集成到我的新存储过程中。

Answers:


144

这样的事情应该起作用:来自Frans Bouma的博客

SELECT * FROM
(
    SELECT a.*, rownum r__
    FROM
    (
        SELECT * FROM ORDERS WHERE CustomerID LIKE 'A%'
        ORDER BY OrderDate DESC, ShippingDate DESC
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE r__ >= (((pageNumber-1) * pageSize) + 1)

4
是的,它是Oracle支持的“内置”列,它始终从1开始,并且每行递增。因此,在此代码段中,如果您有1000行,则将应用排序顺序,然后为每行分配一个rownum。外部选择器使用这些行号根据页面大小找到要查找的“页面”。
布莱恩·施密特

9
这很好,但是在大选择时速度非常慢,只需检查一下选择0到1000和500.000到501.000的时间即可。我正在使用这种选择结构,现在我正在寻找一种解决方法。
newhouse'8

3
@ n3whous3,您可以尝试以下方法-inf.unideb.hu/~gabora/pagination/results.html
jasonk 2012年

7
我想知道为什么WHERE不能将两者结合使用AND,然后发现了这一点:orafaq.com/wiki/ROWNUM
Gao

1
甲骨文分页毁了我的一天。
Aetherus '19

134

问汤姆分页和非常非常有用的分析功能。

这是该页面的摘录:

select * from (
    select /*+ first_rows(25) */
     object_id,object_name,
     row_number() over
    (order by object_id) rn
        from all_objects)
    where rn between :n and :m
        order by rn;

7
尽管很难在那篇文章中找到,但这实际上是一个更好的实现。当您有很多大页面时,另一个答案也必须遍历先前页面的所有行。在复杂的查询中,这意味着后一页的性能比前一页差。
tallseth 2012年

@tallseth你是对的。在该页面上很难找到它。摘录已添加。
Chobicus'4

如果您想动态更改订单,这是正确的答案。
chakeda '18

74

为了完整性,对于寻求更现代解决方案的人们,Oracle 12c提供了一些新功能,包括更好的分页和顶级处理。

分页

分页看起来像这样:

SELECT *
FROM user
ORDER BY first_name
OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY;

前N条记录

获取最高记录如下:

SELECT *
FROM user
ORDER BY first_name
FETCH FIRST 5 ROWS ONLY

请注意,以上两个查询示例均具有ORDER BY子句。新命令遵守这些要求,并在排序后的数据上运行。

我无法找到一个很好的Oracle参考页FETCHOFFSET这个页面有这些新功能很好的概述。

性能

正如@wweicker在下面的注释中指出的那样,性能是12c中新语法的问题。我没有18c的副本来测试Oracle是否从那时开始对其进行了改进。

有趣的是,我第一次在表上运行新方法的查询时(1.13亿多行),我的实际结果返回得更快一些:

  • 新方法:0.013秒。
  • 旧方法:0.107秒。

但是,正如@wweicker所提到的,新方法的解释计划看起来要糟糕得多:

  • 新方法成本:300,110
  • 旧方法成本:30

新语法导致对我的列上的索引进行了全面扫描,这就是全部费用。限制未索引数据的情况有可能变得更糟。

让我们看一下在先前的数据集中包含一个未索引的列的情况:

  • 新方法的时间/成本:189.55秒/ 998,908
  • 旧方法的时间/成本:1.973秒/ 256

摘要:谨慎使用,直到Oracle改进此处理。如果您有要使用的索引,也许可以使用新方法。

希望我很快就会有18c的副本可以使用并可以更新


对于12c用户来说,这是一个很好的答案
Lalji Gajera,

1
语法是清洁,但性能较差(dba-presents.com/index.php/databases/oracle/...
wweicker

很高兴知道,谢谢@wweicker。希望性能很快能被Oracle所确定;尽管了解Oracle,这可能是遥不可及的希望!
JoelC

语法是新的,并且已转换为常规的ROW_NUMBER / RANK调用。相关如何在订购后如何限制Oracle查询返回的行数?
Lukasz Szozda

@JoelC您的意见有任何变化吗?
瑞安

11

只想总结答案和评论。分页有多种方法。

在oracle 12c之前,没有OFFSET / FETCH功能,因此请看@jasonk建议的白皮书。这是我找到的有关不同方法的最完整的文章,详细说明了优缺点。将它们复制粘贴到此处需要花费大量时间,因此我不会这样做。

jooq的创建者也有一篇不错的文章,解释了有关oracle和其他数据库分页的一些常见警告。jooq的博客文章

好消息,自oracle 12c起,我们有了新的OFFSET / FETCH功能。OracleMagazine 12c的新功能。请参考“ Top-N查询和分页”

您可以通过发出以下语句来检查您的oracle版本

SELECT * FROM V$VERSION

7

请尝试以下操作:

SELECT *
FROM
  (SELECT FIELDA,
    FIELDB,
    FIELDC,
    ROW_NUMBER() OVER (ORDER BY FIELDC) R
  FROM TABLE_NAME
  WHERE FIELDA = 10
  )
WHERE R >= 10
AND R   <= 15;

通过[tecnicume]


0

在我的项目中,我使用了Oracle 12c和java。分页代码如下所示:

 public public List<Map<String, Object>> getAllProductOfferWithPagination(int pageNo, int pageElementSize, Long productOfferId, String productOfferName) {
    try {

        if(pageNo==1){
            //do nothing
        } else{
            pageNo=(pageNo-1)*pageElementSize+1;
        }
        System.out.println("algo pageNo: " + pageNo +"  pageElementSize: "+ pageElementSize+"  productOfferId: "+ productOfferId+"  productOfferName: "+ productOfferName);

        String sql = "SELECT * FROM ( SELECT * FROM product_offer po WHERE po.deleted=0 AND (po.product_offer_id=? OR po.product_offer_name LIKE ? )" +
             " ORDER BY po.PRODUCT_OFFER_ID asc) foo OFFSET ? ROWS FETCH NEXT ? ROWS ONLY ";

       return jdbcTemplate.queryForList(sql,new Object[] {productOfferId,"%"+productOfferName+"%",pageNo-1, pageElementSize});

    } catch (Exception e) {
        System.out.println(e);
        e.printStackTrace();
        return null;
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.