有没有办法使一个Oracle查询行为像它包含一个MySQL限制子句?
在MySQL中,我可以这样做:
select *
from sometable
order by name
limit 20,10
要得到第21行到第30行(跳过前20行,给出接下来的10行)。这些行是按顺序选择的,所以实际上是从第20个名字的字母顺序开始的。
在Oracle中,人们唯一提到的是rownum伪列,但它在order by之前求值,这意味着:
select *
from sometable
where rownum <= 10
order by name
将返回一个随机的10行按名称排序的集合,这通常不是我想要的。它也不允许指定偏移量。
从Oracle 12c R1(12.1)开始,有一个行限制子句。它没有使用我们熟悉的LIMIT语法,但是如果有更多的选项,它可以更好地完成这项工作。你可以在这里找到完整的语法。(也可以阅读更多关于如何在Oracle内部工作的答案)。
为了回答最初的问题,下面是问题:
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.
从Oracle 12c R1(12.1)开始,有一个行限制子句。它没有使用我们熟悉的LIMIT语法,但是如果有更多的选项,它可以更好地完成这项工作。你可以在这里找到完整的语法。(也可以阅读更多关于如何在Oracle内部工作的答案)。
为了回答最初的问题,下面是问题:
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.
在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博客上的文章中可以找到更多关于分页查询的信息:
Oracle ROWNUM解释
Top-N和分页查询
我为以下方法做了一些性能测试:
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万条记录,排序在一个未索引的datetime行上:
解释计划对所有三个选择显示相同的值(323168)
但是赢家是AskTom (analytic紧随其后)
选择前10行需要:
AskTom: 28-30秒
分析型:33-37秒
短选择:110-140秒
在100,000和100,010之间选择行:
AskTom: 60秒
分析性:100秒
在9,000,000和9,000,010之间选择行:
AskTom: 130秒
分析型:150秒
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