【问题标题】:Paging with Oracle使用 Oracle 进行分页
【发布时间】:2010-09-19 11:44:40
【问题描述】:

我对 Oracle 的熟悉程度不如我想的那样。我有一些 250k 记录,我想每页显示 100 条记录。目前,我有一个存储过程,它使用数据适配器、数据集和存储过程结果的 dataadapter.Fill(dataset) 方法将一百万条记录的所有四分之一检索到数据集。如果我将“页码”和“每页的记录数”作为整数值,我可以作为参数传递,那么返回该特定部分的最佳方法是什么。比如说,如果我将 10 作为页码传递,将 120 作为页数传递,那么从 select 语句中它会给我第 1880 到第 1200,或者类似的东西,我脑海中的数学可能会出错。

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

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

【问题讨论】:

    标签: sql oracle stored-procedures


    【解决方案1】:

    这样的东西应该可以工作:From Frans Bouma's Blog

    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)
    

    【讨论】:

    • 是的,它是 Oracle 支持的“内置”列,它始终从 1 开始,每行递增。因此,在这段 sn-p 代码中,如果您有 1000 行,则应用排序顺序,然后为每一行分配一个 rownum。外部选择使用这些行号根据您的页面大小来定位您正在寻找的“页面”。
    • 这很好,但在大选择时速度非常慢,只需检查选择 0 到 1000 和 500.000 到 501.000 的时间...我现在正在使用这种选择结构寻找解决方法。
    • @n3whous3 你可以试试这个 - inf.unideb.hu/~gabora/pagination/results.html
    • 我想知道为什么两个WHERE不能和AND结合起来,然后发现:orafaq.com/wiki/ROWNUM
    • Oracle 分页毁了我的一天。
    【解决方案2】:

    Ask Tom 谈分页和非常非常有用的分析功能。

    这是该页面的摘录:

    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;
    

    【讨论】:

    • 这实际上是一个更好的实现,尽管在那个帖子上很难找到。当您有很多大页面时,另一个答案也必须遍历前一页的所有行。在复杂的查询中,这意味着后面的页面比前面的页面执行得更差。
    • @tallseth 你是对的。在那个页面上很难找到它。添加了摘录。
    • 如果您想动态更改订单,这是正确的答案。
    • 嗨,如果在这种情况下我使用“rownum rn”而不是“row_number(...) rn”会发生什么?
    【解决方案3】:

    为了完整起见,对于寻求更现代解决方案的人们,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 子句。新命令尊重这些并在排序后的数据上运行。

    我找不到关于 FETCHOFFSET 的良好 Oracle 参考页面,但 this page 对这些新功能有很好的概述。

    性能

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

    有趣的是,当我第一次对我的表(1.13 亿多行)运行新方法的查询时,我的实际结果返回得稍微快了一点:

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

    然而,正如@wweicker 所提到的,新方法的解释计划看起来更糟糕:

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

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

    让我们看看在前一个数据集上包含单个未索引列时:

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

    总结:在 Oracle 改进此处理之前谨慎使用。如果你有一个索引可以使用,也许你可以使用新方法。

    希望我很快就有 18c 的副本可以玩并且可以更新

    【讨论】:

    【解决方案4】:

    只想总结一下答案和cmets。分页有多种方法。

    在 oracle 12c 之前没有 OFFSET/FETCH 功能,因此请按照 @jasonk 的建议查看 whitepaper。这是我找到的关于不同方法的最完整的文章,并详细解释了优缺点。在这里复制粘贴需要很长时间,所以我不会这样做。

    还有一篇来自 jooq 创建者的好文章,解释了 Oracle 和其他数据库分页的一些常见警告。 jooq's blogpost

    好消息,从 oracle 12c 开始,我们有了新的 OFFSET/FETCH 功能。 OracleMagazine 12c new features。请参考《Top-N 查询与分页》

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

    SELECT * FROM V$VERSION
    

    【讨论】:

      【解决方案5】:

      尝试以下方法:

      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]

      【讨论】:

        【解决方案6】:

        在我的项目中,我使用了 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;
            }
        

        【讨论】:

          猜你喜欢
          • 2010-10-04
          • 2011-03-27
          • 2015-07-09
          • 2019-11-20
          • 2018-08-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多