有没有一种方法可以使Oracle
查询像包含MySQL limit
子句那样工作?
在MySQL
,我可以这样做:
select *
from sometable
order by name
limit 20,10
以获得第21至第30行(跳过前20行,给出下10行)。 这些行是按order by
选择order by
,因此实际上按字母顺序从第20个名称开始。
在Oracle
,人们唯一提到的是rownum
伪列,但它在 order by
之前 order by
进行评估,这意味着:
select *
from sometable
where rownum <= 10
order by name
将返回一个随机的十行,按名称排序,这通常不是我想要的。 它也不允许指定偏移量。
#1楼
在Oracle 12c上(请参阅SQL参考中的行限制子句):
SELECT *
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
#2楼
在Oracle中,带有排序的分页查询确实很棘手。
Oracle提供了一个ROWNUM伪列,该伪列返回一个数字,该数字指示数据库从表或联接视图集中选择行的顺序。
ROWNUM是一个伪列,它使很多人陷入困境。 ROWNUM值未永久分配给行(这是常见的误解)。 实际分配ROWNUM值时可能会造成混淆。 ROWNUM值在通过查询的过滤谓词之后但在查询聚合或排序之前被分配给一行。
此外,仅在分配ROWNUM值后,它才会递增。
这就是为什么followin查询不返回任何行的原因:
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博客上的文章中找到有关分页查询的更多信息:
#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楼
从Oracle 12C R1(12.1)开始, 有一个行限制性条款 。 它没有使用熟悉的LIMIT
语法,但是可以通过更多选项更好地完成这项工作。 您可以在此处找到完整的语法 。
要回答原始问题,以下是查询:
SELECT *
FROM sometable
ORDER BY name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
(对于早期的Oracle版本,请参考此问题中的其他答案)
例子:
链接页面引用了以下示例,以防止链接腐烂。
设定
CREATE TABLE rownum_order_test (
val NUMBER
);
INSERT ALL
INTO rownum_order_test
SELECT level
FROM dual
CONNECT BY level <= 10;
COMMIT;
桌子上有什么?
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.
获取前N
行
SELECT val
FROM rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS ONLY;
VAL
----------
10
10
9
9
8
5 rows selected.
获取前N
行,如果第N
行有联系,则获取所有绑定的行
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.
前x
行百分比
SELECT val
FROM rownum_order_test
ORDER BY val
FETCH FIRST 20 PERCENT ROWS ONLY;
VAL
----------
1
1
2
2
4 rows selected.
使用偏移量,对分页非常有用
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.
您可以将偏移量与百分比结合起来
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.
#5楼
我已经开始为Oracle 1z0-047考试做准备,并针对12c进行了验证。在准备该考试时,我遇到了一种12c增强功能,称为“ FETCH FIRST”。它使您能够根据方便提取行/限制行。 它有几个选项
- 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
#6楼
如果您不在Oracle 12C上,则可以使用TOP N查询,如下所示。
SELECT *
FROM
( SELECT rownum rnum
, a.*
FROM sometable a
ORDER BY name
)
WHERE rnum BETWEEN 10 AND 20;
您甚至可以如下将with子句中的from子句移动
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作为过滤条件。
#7楼
与上述相同,但有更正。 可行,但绝对不漂亮。
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
老实说,最好使用上述答案。
#8楼
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
#9楼
您可以像这样使用子查询
select *
from
( select *
from emp
order by sal desc )
where ROWNUM <= 5;
还可以查看主题ROWNUM和 Oracle / AskTom上的限制结果以获得更多信息。
更新 :为了限制结果的上下限,事情变得更加with肿
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;
有关更多示例,请参见此答案 。 感谢Krumia的提示。
#10楼
(未经测试)像这样的工作
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
还有一个分析功能等级,可用于排序。
#11楼
仅包含一个嵌套查询的解析解决方案:
SELECT * FROM
(
SELECT t.*, Row_Number() OVER (ORDER BY name) MyRow FROM sometable t
)
WHERE MyRow BETWEEN 10 AND 20;
Rank()
可以代替Row_Number()
但如果name值重复,则返回的记录可能比您期望的要多。
#12楼
更少的SELECT语句。 而且,性能消耗更少。 学分至:anibal@upf.br
SELECT *
FROM (SELECT t.*,
rownum AS rn
FROM shhospede t) a
WHERE a.rn >= in_first
AND a.rn <= in_first;
#13楼
对于查询返回的每一行,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
实现了这11.2.0.1.0
#14楼
SQL标准
正如我在本文中所解释的,SQL:2008标准提供了以下语法来限制SQL结果集:
SELECT
title
FROM
post
ORDER BY
id DESC
FETCH FIRST 50 ROWS ONLY
Oracle 11g和更早版本
在版本12c之前,要获取Top-N记录,您必须使用派生表和ROWNUM伪列:
SELECT *
FROM (
SELECT
title
FROM
post
ORDER BY
id DESC
)
WHERE ROWNUM <= 50
#15楼
作为公认答案的扩展,Oracle内部使用ROW_NUMBER/RANK
函数。 OFFSET FETCH
语法是语法糖。
可以通过使用DBMS_UTILITY.EXPAND_SQL_TEXT
过程来观察它:
准备样品:
CREATE TABLE rownum_order_test (
val NUMBER
);
INSERT ALL
INTO rownum_order_test
SELECT level
FROM dual
CONNECT BY level <= 10;
COMMIT;
查询:
SELECT val
FROM rownum_order_test
ORDER BY val DESC
FETCH 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;
获取扩展的SQL文本:
declare
x VARCHAR2(1000);
begin
dbms_utility.expand_sql_text(
input_sql_text => '
SELECT val
FROM rownum_order_test
ORDER BY val DESC
FETCH FIRST 5 ROWS ONLY',
output_sql_text => x);
dbms_output.put_line(x);
end;
/
WITH TIES
扩展为RANK
:
declare
x VARCHAR2(1000);
begin
dbms_utility.expand_sql_text(
input_sql_text => '
SELECT val
FROM rownum_order_test
ORDER BY val DESC
FETCH 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
和偏移量:
declare
x VARCHAR2(1000);
begin
dbms_utility.expand_sql_text(
input_sql_text => '
SELECT val
FROM rownum_order_test
ORDER BY val
OFFSET 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">4
ORDER BY "A1"."rowlimit_$_0"
#16楼
我针对以下方法进行了性能测试:
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秒