现有一个老的项目要从oracle数据库切换到OB,想请教下各位大佬,OB的oracle模式下分页语法是保持和之前oracle一样用rownum伪列分页好,还是用limit、offset分页?
网上资料众说纷纭,有的说rownum效率高,有的说用limit offset效率高,实际啥情况?
项目中基本没有百万级的数据查询,延用oracle的rownum分页语法会有性能问题吗
oracle 早期版本的分页方法 利用 rownum
写两层嵌套循环,这个写法很繁琐。而 mysql 的 limit offset
写法则非常简洁。
oracle 12c 后 oracle 也推出类似的写法 offset N rows fetch next M rows
。
oceanbase 的 oracle 租户兼容了 oracle 的两种分页方法。
从sql 编写上看,肯定是用新的分页方法更方便。对比执行计划,在 OB 4.2.5 版本里,也是新的分页方式性能更好。
- 老的分页方式
explain SELECT * from (SELECT t.*, rownum rn FROM (SELECT o_w_id,o_d_id,o_id,o_c_id,o_cnt FROM bmsql_oorder o ORDER BY o_c_id) t WHERE rownum <=20) WHERE rn > 10;
|0 |COUNT | |10 |1982180 |
|1 |└─SUBPLAN SCAN |VIEW1|10 |1982180 |
|2 | └─LIMIT | |10 |1982180 |
|3 | └─TOP-N SORT | |20 |1982180 |
|4 | └─TABLE FULL SCAN|O |12660010|1160214 |
========================================================
Outputs & filters:
-------------------------------------
0 - output([VIEW1.T.O_W_ID], [VIEW1.T.O_D_ID], [VIEW1.T.O_ID], [VIEW1.T.O_C_ID], [VIEW1.T.O_CNT], [rownum() + cast(cast(FLOOR(10), BIGINT(-1, 0)), NUMBER(-1,
-1))]), filter(nil)
1 - output([VIEW1.T.O_W_ID], [VIEW1.T.O_D_ID], [VIEW1.T.O_ID], [VIEW1.T.O_C_ID], [VIEW1.T.O_CNT]), filter(nil), rowset=256
access([VIEW1.T.O_W_ID], [VIEW1.T.O_D_ID], [VIEW1.T.O_ID], [VIEW1.T.O_C_ID], [VIEW1.T.O_CNT])
2 - output([O.O_W_ID], [O.O_D_ID], [O.O_ID], [O.O_C_ID], [O.O_CNT]), filter(nil), rowset=256
limit(cast(cast(cast(FLOOR(20), BIGINT(-1, 0)), NUMBER(-1, -1)) - cast(cast(FLOOR(10), BIGINT(-1, 0)), NUMBER(-1, -1)), BIGINT(-1, 0))), offset(cast(FLOOR(10),
BIGINT(-1, 0)))
3 - output([O.O_W_ID], [O.O_D_ID], [O.O_ID], [O.O_C_ID], [O.O_CNT]), filter(nil), rowset=256
sort_keys([O.O_C_ID, ASC]), topn(cast(cast(cast(cast(cast(FLOOR(20), BIGINT(-1, 0)), NUMBER(-1, -1)) - cast(cast(FLOOR(10), BIGINT(-1, 0)), NUMBER(-1,
-1)), BIGINT(-1, 0)), NUMBER(-1, -1)) + cast(cast(FLOOR(10), BIGINT(-1, 0)), NUMBER(-1, -1)), BIGINT(-1, 0)))
4 - output([O.O_W_ID], [O.O_D_ID], [O.O_ID], [O.O_C_ID], [O.O_CNT]), filter(nil), rowset=256
access([O.O_W_ID], [O.O_D_ID], [O.O_ID], [O.O_C_ID], [O.O_CNT]), partitions(p0)
is_index_back=false, is_global_index=false,
range_key([O.__pk_increment]), range(MIN ; MAX)always true
- 新的分页方式
explain select o_w_id,o_d_id,o_id,o_c_id,o_cnt from bmsql_oorder ORDER BY o_c_id offset 10 rows fetch next 10 rows only;
===========================================================
|ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)|
-----------------------------------------------------------
|0 |LIMIT | |10 |1982180 |
|1 |└─TOP-N SORT | |20 |1982180 |
|2 | └─TABLE FULL SCAN|BMSQL_OORDER|12660010|1160214 |
===========================================================
Outputs & filters:
-------------------------------------
0 - output([BMSQL_OORDER.O_W_ID], [BMSQL_OORDER.O_D_ID], [BMSQL_OORDER.O_ID], [BMSQL_OORDER.O_C_ID], [BMSQL_OORDER.O_CNT]), filter(nil), rowset=256
limit(cast(FLOOR(10), BIGINT(-1, 0))), offset(cast(FLOOR(10), BIGINT(-1, 0)))
1 - output([BMSQL_OORDER.O_W_ID], [BMSQL_OORDER.O_D_ID], [BMSQL_OORDER.O_ID], [BMSQL_OORDER.O_C_ID], [BMSQL_OORDER.O_CNT]), filter(nil), rowset=256
sort_keys([BMSQL_OORDER.O_C_ID, ASC]), topn(cast(cast(cast(FLOOR(10), BIGINT(-1, 0)), NUMBER(-1, -1)) + cast(cast(FLOOR(10), BIGINT(-1, 0)), NUMBER(-1,
-1)), BIGINT(-1, 0)))
2 - output([BMSQL_OORDER.O_W_ID], [BMSQL_OORDER.O_D_ID], [BMSQL_OORDER.O_ID], [BMSQL_OORDER.O_C_ID], [BMSQL_OORDER.O_CNT]), filter(nil), rowset=256
access([BMSQL_OORDER.O_W_ID], [BMSQL_OORDER.O_D_ID], [BMSQL_OORDER.O_ID], [BMSQL_OORDER.O_C_ID], [BMSQL_OORDER.O_CNT]), partitions(p0)
is_index_back=false, is_global_index=false,
range_key([BMSQL_OORDER.__pk_increment]), range(MIN ; MAX)always true
学习了,oracle 优化了这个都不知道。
分页性能上可能会出问题吧
使用rownum有性能问题?offset在深分页情况下也要跳过大量行,这个说是也有性能问题
大佬是推荐用offset fetch?offset在深分页情况下需要跳过大量行,这个会有性能问题吗?官方文档上说是offset跳过大量数据性能会下降
个人对oracle的分析 不代表官方 非ob oracle模式下 自己可以在ob oracle模式下 测试一下
1、伪列分页
select column_lists from
(select rownum as rn,A.* from
(select column_lists from table_name where col_1=:b0) A where rownum<=:b2
) where rn>:b1;
2、使用row_number() over分析函数,
select * from (select a.*, row_number() over (order by empno) as rn from emp a) where rn between 5 and 10;`
3、oracle 12c 中使用了简洁的offset 语法
select * from emp order by empno offset 4 rows fetch next 5 rows only;
我记得这个内部是row_number() over()的改写 不过这个排序要做索引 offset的值尽量要大致确定好对于非常大的OFFSET值,可能需要遍历许多无关的行才能达到OFFSET的位置
这几种方式分页 其实到了百万行 还分页其实性能也会有折扣 oracle12C之前没有标准的sql分页 offset这个写法 比较简洁 。
这个知识牵扯ob的企业版,你可以咨询企业版客户经理,另外,我们欢迎你使用社区版,并在论坛/社群中分享你对社区版本的想法、经验和问题,与其他社区成员共同交流。