如何在订购后限制Oracle查询返回的行数?

问题描述 投票:908回答:14

有没有办法让Oracle查询表现得像包含MySQL limit子句?

MySQL,我可以这样做:

select * 
from sometable
order by name
limit 20,10

获得第21行到第30行(跳过前20行,给出下一行10)。在order by之后选择行,所以它实际上是按字母顺序从第20个名称开始的。

Oracle,人们唯一提到的是rownum伪列,但它在order by之前进行了评估,这意味着:

select * 
from sometable
where rownum <= 10
order by name

将返回按名称排序的十行的随机集合,这通常不是我想要的。它也不允许指定偏移量。

sql oracle pagination limit
14个回答
489
投票

从Oracle 12c R1(12.1)开始,有一个row limiting clause。它没有使用熟悉的LIMIT语法,但它可以通过更多选项更好地完成工作。你可以找到full syntax here

要回答原始问题,这是查询:

SELECT * 
FROM   sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

(对于早期的Oracle版本,请参阅此问题中的其他答案)


例子:

下面的例子引自linked page,希望防止链接腐烂。

Setup

CREATE TABLE rownum_order_test (
  val  NUMBER
);

INSERT ALL
  INTO rownum_order_test
SELECT level
FROM   dual
CONNECT BY level <= 10;

COMMIT;

What's in the table?

SELECT val
FROM   rownum_order_test
ORDER BY val;

       VAL
----------
         1
         1
         2
         2
         3
         3
         4
         4
         5
         5
         6
         6
         7
         7
         8
         8
         9
         9
        10
        10

20 rows selected.

Get first N rows

SELECT val
FROM   rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS ONLY;

       VAL
----------
        10
        10
         9
         9
         8

5 rows selected.

Get first N rows, if Nth row has ties, get all the tied rows

SELECT val
FROM   rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS WITH TIES;

       VAL
----------
        10
        10
         9
         9
         8
         8

6 rows selected.

Top x% of rows

SELECT val
FROM   rownum_order_test
ORDER BY val
FETCH FIRST 20 PERCENT ROWS ONLY;

       VAL
----------
         1
         1
         2
         2

4 rows selected.

Using an offset, very useful for pagination

SELECT val
FROM   rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;

       VAL
----------
         3
         3
         4
         4

4 rows selected.

You can combine offset with percentages

SELECT val
FROM   rownum_order_test
ORDER BY val
OFFSET 4 ROWS FETCH NEXT 20 PERCENT ROWS ONLY;

       VAL
----------
         3
         3
         4
         4

4 rows selected.

1
投票

我开始准备Oracle 1z0-047考试,对12c进行验证在准备它时我遇到了一个名为'FETCH FIRST'的12c增强功能它可以让你根据自己的方便来获取行/限制行。有几个选项可供选择

- FETCH FIRST n ROWS ONLY
 - OFFSET n ROWS FETCH NEXT N1 ROWS ONLY // leave the n rows and display next N1 rows
 - n % rows via FETCH FIRST N PERCENT ROWS ONLY

例:

Select * from XYZ a
order by a.pqr
FETCH FIRST 10 ROWS ONLY

1
投票

SQL标准

正如我在this article中解释的那样,SQL:2008 Standard提供了以下语法来限制SQ​​L结果集:

SELECT
    title
FROM
    post
ORDER BY
    id DESC
FETCH FIRST 50 ROWS ONLY

Oracle 11g及更早版本

在版本12c之前,要获取前N个记录,您必须使用派生表和ROWNUM伪列:

SELECT *
FROM (
    SELECT
        title
    FROM
        post
    ORDER BY
        id DESC
)
WHERE ROWNUM <= 50

0
投票

对于查询返回的每一行,ROWNUM伪列返回一个数字,表示Oracle从一个表或一组连接行中选择行的顺序。选择的第一行的ROWNUM为1,第二行的数量为2,依此类推。

  SELECT * FROM sometable1 so
    WHERE so.id IN (
    SELECT so2.id from sometable2 so2
    WHERE ROWNUM <=5
    )
    AND ORDER BY so.somefield AND ROWNUM <= 100 

我在oracle服务器11.2.0.1.0中实现了这一点


-3
投票

在甲骨文

SELECT val FROM   rownum_order_test ORDER BY val DESC FETCH FIRST 5 ROWS ONLY;

VAL

    10
    10
     9
     9
     8

选择了5行。

SQL>


-4
投票

(未经测试)这样的事情可能会起作用

WITH
base AS
(
    select *                   -- get the table
    from sometable
    order by name              -- in the desired order
),
twenty AS
(
    select *                   -- get the first 30 rows
    from base
    where rownum < 30
    order by name              -- in the desired order
)
select *                       -- then get rows 21 .. 30
from twenty
where rownum > 20
order by name                  -- in the desired order

还有分析函数等级,您可以使用它来排序。


-5
投票

与上述相同,有更正。工作,但绝对不漂亮。

   WITH
    base AS
    (
        select *                   -- get the table
        from sometable
        order by name              -- in the desired order
    ),
    twenty AS
    (
        select *                   -- get the first 30 rows
        from base
        where rownum <= 30
        order by name              -- in the desired order
    )
    select *                       -- then get rows 21 .. 30
    from twenty
    where rownum < 20
    order by name                  -- in the desired order

老实说,最好使用上面的答案。


763
投票

您可以使用子查询

select *
from  
( select * 
  from emp 
  order by sal desc ) 
where ROWNUM <= 5;

还可以查看Oracle / AskTom上的On ROWNUM and limiting results主题以获取更多信息。

更新:为了限制下限和上限的结果,事情会变得更加臃肿

select * from 
( select a.*, ROWNUM rnum from 
  ( <your_query_goes_here, with order by> ) a 
  where ROWNUM <= :MAX_ROW_TO_FETCH )
where rnum  >= :MIN_ROW_TO_FETCH;

(从指定的AskTom文章复制)

更新2:从Oracle 12c(12.1)开始,有一种语法可用于限制行或从偏移量开始。

SELECT * 
FROM   sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

有关更多示例,请参阅this answer。感谢Krumia的暗示。


174
投票

我对以下方法进行了一些性能测试:

Asktom

select * from (
  select a.*, ROWNUM rnum from (
    <select statement with order by clause>
  ) a where rownum <= MAX_ROW
) where rnum >= MIN_ROW

Analytical

select * from (
  <select statement with order by clause>
) where myrow between MIN_ROW and MAX_ROW

Short Alternative

select * from (
  select statement, rownum as RN with order by clause
) where a.rn >= MIN_ROW and a.rn <= MAX_ROW

Results

表有1000万条记录,排序在未编入索引的日期时间行:

  • 解释计划显示所有三个选择的相同值(323168)
  • 但获胜者是AskTom(分析紧随其后)

选择前10行:

  • AskTom:28-30秒
  • 分析:33-37秒
  • 短期替代方案:110-140秒

选择100,000到100,010之间的行:

  • AskTom:60秒
  • 分析:100秒

选择9,000,000到9,000,010之间的行:

  • AskTom:130秒
  • 分析:150秒

53
投票

只有一个嵌套查询的分析解决方案:

SELECT * FROM
(
   SELECT t.*, Row_Number() OVER (ORDER BY name) MyRow FROM sometable t
) 
WHERE MyRow BETWEEN 10 AND 20;

Rank()可以替代Row_Number(),但如果名称有重复值,可能会返回比预期更多的记录。


28
投票

在Oracle 12c上(参见SQL reference中的行限制子句):

SELECT * 
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

13
投票

在Oracle中,带有排序的分页查询非常棘手。

Oracle提供了一个ROWNUM伪列,它返回一个数字,指示数据库从一个表或一组连接视图中选择行的顺序。

ROWNUM是一个伪列,让许多人陷入困境。 ROWNUM值不会永久分配给一行(这是一种常见的误解)。实际分配ROWNUM值时可能会造成混淆。在通过查询的筛选谓词但在查询聚合或排序之前,将ROWNUM值分配给行。

更重要的是,ROWNUM值仅在分配后才会递增。

这就是以下查询不返回任何行的原因:

 select * 
 from (select *
       from some_table
       order by some_column)
 where ROWNUM <= 4 and ROWNUM > 1; 

查询结果的第一行不传递ROWNUM> 1谓词,因此ROWNUM不会增加到2.因此,没有ROWNUM值大于1,因此查询不返回任何行。

正确定义的查询应如下所示:

select *
from (select *, ROWNUM rnum
      from (select *
            from skijump_results
            order by points)
      where ROWNUM <= 4)
where rnum > 1; 

在我的Vertabelo博客文章中了解有关分页查询的更多信息:


5
投票

少SELECT语句。此外,性能消耗较少。致记:[email protected]

SELECT *
    FROM   (SELECT t.*,
                   rownum AS rn
            FROM   shhospede t) a
    WHERE  a.rn >= in_first
    AND    a.rn <= in_first;

2
投票

如果您不在Oracle 12C上,则可以使用如下所示的TOP N查询。

SELECT *
 FROM
   ( SELECT rownum rnum
          , a.*
       FROM sometable a 
   ORDER BY name
   )
WHERE rnum BETWEEN 10 AND 20;

您甚至可以从with子句中的子句移动它,如下所示

WITH b AS
( SELECT rownum rnum
      , a.* 
   FROM sometable a ORDER BY name
) 
SELECT * FROM b 
WHERE rnum BETWEEN 10 AND 20;

实际上,我们创建了一个内联视图,并将rownum重命名为rnum。您可以在主查询中使用rnum作为过滤条件。


2
投票
select * FROM (SELECT 
   ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
 FROM EMP ) EMP  where ROWID=5

更大的价值找出来

select * FROM (SELECT 
       ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
     FROM EMP ) EMP  where ROWID>5

少发现值

select * FROM (SELECT 
       ROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID, 
     FROM EMP ) EMP  where ROWID=5
© www.soinside.com 2019 - 2024. All rights reserved.