[Oracle] ROWNUM和分页

时间:2021-11-10 23:44:17

rownum是oracle的一个伪劣,它的顺序依据从表中获取记录的顺序递增,这里要注意的是:由于记录在表中是无序存放的。因此你无法通过简单的rownum和order by的组合获得相似TOP N的结果。

我们的測试数据例如以下:

 select * from test;

        ID NAME
---------- --------------------
1 A
3 C
4 C
8 C
10 D
2 B
5 C
7 C
6 C
9 D

通过rownum<=5能够获得前5行数据:

select * from test where rownum<=5;

        ID NAME
---------- --------------------
1 A
3 C
4 C
8 C
10 D

假设你想获得像top n那样的结果。必须使用子查询:

select * from (select * from test order by id) where rownum<=5;

        ID NAME
---------- --------------------
1 A
2 B
3 C
4 C
5 C

假设你想获得第5行到第10行之间的数据,则必须再加一层子查询:

 select T.* from (select t.*,rownum rn from (select * from test order by id) t where rownum<=10) T where T.rn>5;

        ID NAME                         RN
---------- -------------------- ----------
6 C 6
7 C 7
8 C 8
9 D 9
10 D 10

事实上上面的写法是由陷阱的。不信你把order by id换成order by name试试看:

select * from (select * from test order by name) where rownum<=5;

        ID NAME
---------- --------------------
1 A
2 B
3 C
4 C
8 C
select T.* from (select t.*,rownum rn from (select * from test order by name) t where rownum<=10) T where T.rn>5; ID NAME RN
---------- -------------------- ----------
5 C 6
4 C 7
8 C 8
10 D 9
9 D 10

你会惊奇的发现id=4这条数据出如今了两个地方,这不合逻辑!但事实就是这种,为什么呢?由于name不唯一,两次排序取出的结果有可能会不一样,我还是举个样例吧:

 select id,name,rank() over(order by name) from test;

        ID NAME                 RANK()OVER(ORDERBYNAME)
---------- -------------------- -----------------------
1 A 1
2 B 2
6 C 3
3 C 3
4 C 3
8 C 3
5 C 3
7 C 3
9 D 9
10 D 9

从上面的结果我们不难发现。依据name排序,有多条数据并列排在第3位。这样。当取前5名时,究竟在并列第3中取哪几位就不是确定的事。因此就出现了之前出现的诡异的问题。那么,如何才干彻底解决问题呢?事实上仅仅要在order by name后面加上rowid,保证不会出现并列的情况就能够了,例如以下所看到的:

 select * from (select * from test order by name,rowid) where rownum<=5;

        ID NAME
---------- --------------------
1 A
2 B
3 C
4 C
8 C
select T.* from (select t.*,rownum rn from (select * from test order by name,rowid) t where rownum<=10) T where T.rn>5; ID NAME RN
---------- -------------------- ----------
5 C 6
7 C 7
6 C 8
10 D 9
9 D 10