与Oracle进行分页

时间:2008-10-27 22:37:21

标签: sql oracle stored-procedures

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

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

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

6 个答案:

答案 0 :(得分:125)

这样的事情应该有效: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)

答案 1 :(得分:122)

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;

答案 2 :(得分:54)

为了完整性,对于寻求更现代化解决方案的人来说,在 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在下面的评论中指出的,性能是12c中新语法的问题。我没有18c的副本来测试Oracle是否已经改进了它。

有趣的是,第一次在我的桌子上运行查询(1.13亿行)时,我的实际结果返回得稍快一点:

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

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

  • 新方法费用:300,110
  • 旧方法费用:30

新语法导致我的列上的索引完全扫描,这是整个成本。机会是,当限制未编制索引的数据时,情况会变得更糟。

让我们看看在前一个数据集中包含一个未编制索引的列:

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

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

希望我很快就能获得18c的副本并且可以更新

答案 3 :(得分:10)

只想总结一下答案和评论。有很多方法可以进行分页。

在oracle 12c之前没有OFFSET / FETCH功能,所以请查看@ {jasonk建议的whitepaper。这是我发现的关于不同方法的最完整的文章,详细解释了优缺点。这里将它们复制粘贴需要花费大量时间,所以我不会这样做。

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

好消息,因为o​​racle 12c我们有一个新的OFFSET / FETCH功能。 OracleMagazine 12c new features。请参阅&#34;前N个查询和分页&#34;

您可以通过发出以下声明来检查您的oracle版本

SELECT * FROM V$VERSION

答案 4 :(得分: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]

答案 5 :(得分: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;
    }