分页语句方法:
1.rownum
2.row_number over(order by column) as 行号
例子:
create table page as select * from dba_objects;
create index idx_page_1 on page(object_id,owner);
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(ownname => 'QUERY',
tabname => 'PAGE',
estimate_percent => 100,
method_opt => 'for all columns size skewonly',
no_invalidate => FALSE,
degree => 4,
cascade => TRUE);
END;
/
比如这个SQL
select object_id from
(
select * from
(
select a.*,rownum rn
from page a
where object_id >1000 and owner='SYS'
order by object_id
) where rownum<=20
) where rn>=0;
SQL> select min(object_id) ,max(object_id) from page where owner='SYS' and object_id>1000;
MIN(OBJECT_ID) MAX(OBJECT_ID)
-------------- --------------
1001 111148
SQL> set linesize 200
SQL> set pagesize 200
SQL> alter session set statistics_level=all;
会话已更改。
SQL> select object_id from
(
select * from
(
select a.*,rownum rn
from page a
where object_id >1000 and owner='SYS'
order by object_id
) where rownum<=20
) where rn>=0; 2 3 4 5 6 7 8 9 10
OBJECT_ID
----------
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
已选择20行。
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID dfb5uh3bgs11m, child number 0
-------------------------------------
select object_id from ( select * from ( select a.*,rownum rn from
page a where object_id >1000 and owner='SYS' order by object_id )
where rownum<=20 ) where rn>=0
Plan hash value: 2584750597
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.01 | 4 |
|* 1 | VIEW | | 1 | 20 | 20 |00:00:00.01 | 4 |
|* 2 | COUNT STOPKEY | | 1 | | 20 |00:00:00.01 | 4 |
| 3 | VIEW | | 1 | 37466 | 20 |00:00:00.01 | 4 |
| 4 | COUNT | | 1 | | 20 |00:00:00.01 | 4 |
|* 5 | INDEX RANGE SCAN| IDX_PAGE_1 | 1 | 37466 | 20 |00:00:00.01 | 4 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RN">=0)
2 - filter(ROWNUM<=20)
5 - access("OBJECT_ID">1000 AND "OWNER"='SYS' AND "OBJECT_ID" IS NOT NULL)
filter("OWNER"='SYS')
已选择27行。
ORACLE升序扫描了索引,扫描20条记录就停止了.
继续取下一页,
SQL> select object_id from
(
select * from
(
select a.*,rownum rn
from page a
where object_id >1000 and owner='SYS'
order by object_id
) where rownum<=40
) where rn>=21; 2 3 4 5 6 7 8 9 10
OBJECT_ID
----------
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
已选择20行。
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID ftvyz3pxdfvyf, child number 0
-------------------------------------
select object_id from ( select * from ( select a.*,rownum rn from
page a where object_id >1000 and owner='SYS' order by object_id )
where rownum<=40 ) where rn>=21
Plan hash value: 2584750597
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.01 | 4 |
|* 1 | VIEW | | 1 | 40 | 20 |00:00:00.01 | 4 |
|* 2 | COUNT STOPKEY | | 1 | | 40 |00:00:00.01 | 4 |
| 3 | VIEW | | 1 | 37466 | 40 |00:00:00.01 | 4 |
| 4 | COUNT | | 1 | | 40 |00:00:00.01 | 4 |
|* 5 | INDEX RANGE SCAN| IDX_PAGE_1 | 1 | 37466 | 40 |00:00:00.01 | 4 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RN">=21)
2 - filter(ROWNUM<=40)
5 - access("OBJECT_ID">1000 AND "OWNER"='SYS' AND "OBJECT_ID" IS NOT NULL)
filter("OWNER"='SYS')
已选择27行。
可以看到取下一页20行时,需要访问40条记录
继续取下一页
SQL> select object_id from
(
select * from
(
select a.*,rownum rn
from page a
where object_id >1000 and owner='SYS'
order by object_id
) where rownum<=60
) where rn>=41; 2 3 4 5 6 7 8 9 10
OBJECT_ID
----------
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
已选择20行。
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 9xj40t5wq2n8v, child number 0
-------------------------------------
select object_id from ( select * from ( select a.*,rownum rn from
page a where object_id >1000 and owner='SYS' order by object_id )
where rownum<=60 ) where rn>=41
Plan hash value: 2584750597
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.01 | 4 |
|* 1 | VIEW | | 1 | 60 | 20 |00:00:00.01 | 4 |
|* 2 | COUNT STOPKEY | | 1 | | 60 |00:00:00.01 | 4 |
| 3 | VIEW | | 1 | 37466 | 60 |00:00:00.01 | 4 |
| 4 | COUNT | | 1 | | 60 |00:00:00.01 | 4 |
|* 5 | INDEX RANGE SCAN| IDX_PAGE_1 | 1 | 37466 | 60 |00:00:00.01 | 4 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RN">=41)
2 - filter(ROWNUM<=60)
5 - access("OBJECT_ID">1000 AND "OWNER"='SYS' AND "OBJECT_ID" IS NOT NULL)
filter("OWNER"='SYS')
已选择27行。
取第三页时需要访问60条记录
下面进行降序排序:
SQL> select object_id from
(
select * from
(
select a.*,rownum rn
from page a
where object_id >1000 and owner='SYS'
order by object_id desc
) where rownum<=20
) where rn>=0; 2 3 4 5 6 7 8 9 10
OBJECT_ID
----------
111148
111147
111146
111122
111121
111098
111097
111075
111074
111050
111049
111025
111024
111000
110999
110975
110974
110950
110949
110924
已选择20行。
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID fbp9fbq0suc6c, child number 0
-------------------------------------
select object_id from ( select * from ( select a.*,rownum rn from
page a where object_id >1000 and owner='SYS' order by object_id desc
) where rownum<=20 ) where rn>=0
Plan hash value: 512348098
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.01 | 4 |
|* 1 | VIEW | | 1 | 20 | 20 |00:00:00.01 | 4 |
|* 2 | COUNT STOPKEY | | 1 | | 20 |00:00:00.01 | 4 |
| 3 | VIEW | | 1 | 37466 | 20 |00:00:00.01 | 4 |
| 4 | COUNT | | 1 | | 20 |00:00:00.01 | 4 |
|* 5 | INDEX RANGE SCAN DESCENDING| IDX_PAGE_1 | 1 | 37466 | 20 |00:00:00.01 | 4 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RN">=0)
2 - filter(ROWNUM<=20)
5 - access("OBJECT_ID" IS NOT NULL AND "OBJECT_ID">1000 AND "OWNER"='SYS')
filter("OWNER"='SYS')
已选择27行。
ORACLE降序扫描了索引,扫描20条记录就停止了.
继续降序取下一页:
SQL> select object_id from
(
select * from
(
select a.*,rownum rn
from page a
where object_id >1000 and owner='SYS'
order by object_id desc
) where rownum<=40
) where rn>=21;
2 3 4 5 6 7 8 9 10
OBJECT_ID
----------
109638
109637
105456
105455
104527
104526
104415
94031
87442
87441
87440
87439
87438
87437
87436
87435
87434
87433
87432
87431
已选择20行。
SQL>
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 09ryhg66tgad5, child number 0
-------------------------------------
select object_id from ( select * from ( select a.*,rownum rn from
page a where object_id >1000 and owner='SYS' order by object_id desc
) where rownum<=40 ) where rn>=21
Plan hash value: 512348098
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 20 |00:00:00.01 | 31 |
|* 1 | VIEW | | 1 | 40 | 20 |00:00:00.01 | 31 |
|* 2 | COUNT STOPKEY | | 1 | | 40 |00:00:00.01 | 31 |
| 3 | VIEW | | 1 | 37466 | 40 |00:00:00.01 | 31 |
| 4 | COUNT | | 1 | | 40 |00:00:00.01 | 31 |
|* 5 | INDEX RANGE SCAN DESCENDING| IDX_PAGE_1 | 1 | 37466 | 40 |00:00:00.01 | 31 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RN">=21)
2 - filter(ROWNUM<=40)
5 - access("OBJECT_ID" IS NOT NULL AND "OBJECT_ID">1000 AND "OWNER"='SYS')
filter("OWNER"='SYS')
已选择27行。
这个时候降序分页取下一页需要访问40条记录。
总结:
1.分页语句必须走索引,在分页语句的WHERE 条件列和order by列上创建组合索引
2.分页语句两边快,越到中间越慢,因为索引要访问的记录更多