如何限制排序后Oracle查询返回的行数?

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

在MySQL中,我可以这样做:

select *from sometableorder by namelimit 20,10

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

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

select *from sometablewhere rownum <= 10order by name

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

2065020 次浏览

您可以为此使用子查询,例如

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

还可以查看Oracle/AskTom上的主题关于ROWNUM和限制性结果以获取更多信息。

更新:为了限制下限和上限的结果,使用

会让事情变得更加臃肿
select * from( select a.*, ROWNUM rnum from( <your_query_goes_here, with order by> ) awhere ROWNUM <= :MAX_ROW_TO_FETCH )where rnum  >= :MIN_ROW_TO_FETCH;

(从指定的AskTom文章复制)

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

SELECT *FROM   sometableORDER BY nameOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

有关更多示例,请参阅这个答案。感谢Krumia的提示。

(未经测试)像这样的东西可能会做这项工作

WITHbase AS(select *                   -- get the tablefrom sometableorder by name              -- in the desired order),twenty AS(select *                   -- get the first 30 rowsfrom basewhere rownum < 30order by name              -- in the desired order)select *                       -- then get rows 21 .. 30from twentywhere rownum > 20order by name                  -- in the desired order

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

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

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

Rank()可以替换Row_Number(),但如果name有重复值,则可能返回比预期更多的记录。

更少的SELECT语句。同时,更少的性能消耗。归功于:anibal@upf.br

SELECT *FROM   (SELECT t.*,rownum AS rnFROM   shhospede t) aWHERE  a.rn >= in_firstAND    a.rn <= in_first;

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

Asktom

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

分析

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

短替代品

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

搜索结果

表有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秒

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

SELECT *FROM sometableORDER BY nameOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

排序分页查询在Oracle中非常棘手。

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

ROWNUM是一个让许多人陷入困境的伪列。ROWNUM值不会永久分配给一行(这是一个常见的误解)。实际分配ROWNUM值时可能会令人困惑。ROWNUM值分配给查询的第0行,但在查询聚合或排序之前

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

这就是为什么后续查询不返回任何行:

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

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

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

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

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

从Oracle 12c R1(12.1)开始,有行限制子句。它不使用熟悉的LIMIT语法,但它可以通过更多选项更好地完成工作。您可以找到完整语法在这里。(另请在这个答案中阅读有关Oracle内部如何工作的更多信息)。

为了回答最初的问题,这里是查询:

SELECT *FROM   sometableORDER BY nameOFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

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


示例:

下面的例子是从链接页面引用的,希望能防止链接腐烂。

设置

CREATE TABLE rownum_order_test (val  NUMBER);
INSERT ALLINTO rownum_order_testSELECT levelFROM   dualCONNECT BY level <= 10;
COMMIT;

桌子里有什么?

SELECT valFROM   rownum_order_testORDER BY val;
VAL----------1122334455667788991010
20 rows selected.

获取第一个N

SELECT valFROM   rownum_order_testORDER BY val DESCFETCH FIRST 5 ROWS ONLY;
VAL----------1010998
5 rows selected.

获取第一个N行,如果Nth行有领带,则获取所有领带行

SELECT valFROM   rownum_order_testORDER BY val DESCFETCH FIRST 5 ROWS WITH TIES;
VAL----------10109988
6 rows selected.

顶部x%的行

SELECT valFROM   rownum_order_testORDER BY valFETCH FIRST 20 PERCENT ROWS ONLY;
VAL----------1122
4 rows selected.

使用偏移量,对分页非常有用

SELECT valFROM   rownum_order_testORDER BY valOFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;
VAL----------3344
4 rows selected.

您可以将偏移量与百分比组合

SELECT valFROM   rownum_order_testORDER BY valOFFSET 4 ROWS FETCH NEXT 20 PERCENT ROWS ONLY;
VAL----------3344
4 rows selected.

我开始准备Oracle 1z0-047考试,针对12c进行验证在准备的时候,我遇到了一个被称为“FETCH First”的12c增强它使您能够根据您的方便获取行 /limit行。有几个选项可供选择

- 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 aorder by a.pqrFETCH FIRST 10 ROWS ONLY
select * FROM (SELECTROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID,FROM EMP ) EMP  where ROWID=5

然后值就会发现

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

更少的价值观发现

select * FROM (SELECTROW_NUMBER() OVER (ORDER BY sal desc),* AS ROWID,FROM EMP ) EMP  where ROWID=5

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

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

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

SQL标准

由于版本12c Oracle支持SQL: 2008 Standard,它提供了以下语法来限制SQL结果集:

SELECTtitleFROMpostORDER BYid DESCFETCH FIRST 50 ROWS ONLY

Oracle 11g及更早版本

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

SELECT *FROM (SELECTtitleFROMpostORDER BYid DESC)WHERE ROWNUM <= 50

作为接受的答案的扩展,Oracle内部使用ROW_NUMBER/RANK函数。OFFSET FETCH语法是语法糖。

它可以通过使用#0过程来观察:

准备样品:

CREATE TABLE rownum_order_test (val  NUMBER);
INSERT ALLINTO rownum_order_testSELECT levelFROM   dualCONNECT BY level <= 10;COMMIT;

查询:

SELECT valFROM   rownum_order_testORDER BY val DESCFETCH FIRST 5 ROWS ONLY;

有规律:

SELECT "A1"."VAL" "VAL"FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",ROW_NUMBER() OVER ( ORDER BY "A2"."VAL" DESC ) "rowlimit_$$_rownumber"FROM "ROWNUM_ORDER_TEST" "A2") "A1"WHERE "A1"."rowlimit_$$_rownumber"<=5 ORDER BY "A1"."rowlimit_$_0" DESC;

db<>小提琴演示

获取扩展SQL文本:

declarex VARCHAR2(1000);begindbms_utility.expand_sql_text(input_sql_text => 'SELECT valFROM   rownum_order_testORDER BY val DESCFETCH FIRST 5 ROWS ONLY',output_sql_text => x);
dbms_output.put_line(x);end;/

WITH TIES扩展为RANK

declarex VARCHAR2(1000);begindbms_utility.expand_sql_text(input_sql_text => 'SELECT valFROM   rownum_order_testORDER BY val DESCFETCH FIRST 5 ROWS WITH TIES',output_sql_text => x);
dbms_output.put_line(x);end;/
SELECT "A1"."VAL" "VAL"FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",RANK() OVER ( ORDER BY "A2"."VAL" DESC ) "rowlimit_$$_rank"FROM "ROWNUM_ORDER_TEST" "A2") "A1"WHERE "A1"."rowlimit_$$_rank"<=5 ORDER BY "A1"."rowlimit_$_0" DESC

和抵消:

declarex VARCHAR2(1000);begindbms_utility.expand_sql_text(input_sql_text => 'SELECT valFROM   rownum_order_testORDER BY valOFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY',output_sql_text => x);
dbms_output.put_line(x);end;/

SELECT "A1"."VAL" "VAL"FROM  (SELECT "A2"."VAL" "VAL","A2"."VAL" "rowlimit_$_0",ROW_NUMBER() OVER ( ORDER BY "A2"."VAL") "rowlimit_$$_rownumber"FROM "ROWNUM_ORDER_TEST" "A2") "A1"WHERE "A1"."rowlimit_$$_rownumber"<=CASE  WHEN (4>=0) THEN FLOOR(TO_NUMBER(4))ELSE 0 END +4 AND "A1"."rowlimit_$$_rownumber">4ORDER BY "A1"."rowlimit_$_0"

使用21c版本,您可以简单地应用如下限制:

select * from course where ROWNUM <=10;