Oracle 实现分页查询

版权声明:转发请注明链接和出处,靴靴 https://blog.csdn.net/weixin_43525116/article/details/85006795

当数据量太大,例如咱们数据库的一个表里,符合查询条件的有几十万条数据,如果你一次性查询出来的话,不仅查询速度很慢,响应时间太长影响用户体验,而且用户也看不了这么多数据。

可能有人会说可以在后台进行分页类封装处理(逻辑分页),那你首先要把查询到的几十万条数据存在内存里,这样看来,还是推荐数据库分页查询(物理分页)吧

一、Oracle采用ROWNUM实现分页

两种格式的查询效率对比(格式1更快速)

首先,两种格式都有三层查询。
CBO优化模式下,Oracle可以将外层的查询条件,推到内层(只能向内推一层)查询中,以提高内层查询的执行效率。
对于格式1的第二层查询条件,WHERE ROWNUM <=end (page*pagesize) 就可以被推入内层查询中,这样的话,Oracle查询的结果一旦超过了ROWNUM的限制条件,就终止查询,将结果返回了。

-- 格式1(推荐)
SELECT * FROM   
(  
SELECT temp.*, ROWNUM RN   
FROM (SELECT * FROM 表名) temp  
WHERE ROWNUM <=end (page*pagesize)  
)  
WHERE RN >=start (page-1)*pagesize

但是格式2的ROWNUM限制条件,是放在了第三层的查询语句中,Oracle无法把第三层的内推到第一层(即使推到最内层也没有意义,因为最内层不知道RN是啥,RN是定义在第二层的)

-- 格式2
SELECT * FROM   
(  
SELECT temp.*, ROWNUM RN   
FROM (SELECT * FROM TABLE_NAME) temp   
)  
WHERE RN BETWEEN start (page-1)*pagesize AND end (page*pagesize)

因此,对于格式2查询语句,最内层返回给第二层所有符合条件的数据,第二层返回给第三层所有符合条件的数据,过滤数据发生在第三层;而格式1,在第一层的时候就将数据过滤好了,再将量减少了的数据向外返回,是不是效率自然就高了起来。

注意:上述两种格式,针对最内层是复杂多表联合的查询,也同样适用。

UNIONUNION ALLMINUSINTERSECTGROUP BYDISTINCTUNIQUE以及聚集函数如MAXMIN分析函数等操作是针对,全部符合条件的数据的结果集,的一个操作,所以如果最内层的子查询中包含了这些操作中的一个以上,那么ROWNUM会不起作用,分页查询的性能等于没实现。

Oracle10g的新功能GROUP BY STOPKEY,使得Oracle10g解决了GROUP BY操作分页效率低的问题。在10g以前,Oracle的GROUP BY操作必须完全执行完,才能将结果返回给用户。但是Oracle10g增加了GROUP BY STOPKEY执行路径,使得用户在执行GROUP BY操作时,可以根据STOPKEY随时中止正在运行的操作。这使得标准分页函数对于GROUP BY操作重新发挥了作用。

二、ROWNUM

操作执行原理

  1. 执行查询操作;
  2. 将第一行的ROWNUM置为1;
  3. 将得到的行的ROWNUM与条件相比较,如果不匹配,则抛弃行,如果匹配,则返回行;
  4. oracle获取下一行,然后将ROWNUM增1;
  5. 返回第3步;
  6. 直到超过ROWNUM的限制条件;

ROWNUM 使用注意事项

  1. ROWNUM不能以任何基表的名称作为前缀。
  2. 子查询中的ROWNUM必须要有别名,否则还是不会查出记录来,这是因为ROWNUM不是某个表的列,如果不起别名的话,无法知道ROWNUM是子查询的列还是主查询的列。
  3. 查询ROWNUM在某区间的数据,ROWNUM对小于某值的查询条件为true,对于大于某值的查询条件直接认为是false的,但是可以间接的让它转为认为是true的。那就必须使用子查询。

三、FIRST_ROWS

对于表连接来说,在写分页查询的时候,可以考虑增加FIRST_ROWS提示,它会导致CBO选择NESTED LOOP,有助于更快的将查询结果返回。
其实,不光是表连接,对于所有的分页查询都可以加上FIRST_ROWS提示。

-- 格式3
SELECT FIRST_ROWS * FROM   
(  
SELECT temp.*, ROWNUM RN   
FROM (SELECT * FROM TABLE_NAME) temp   
)  
WHERE RN BETWEEN start (page-1)*pagesize AND end (page*pagesize)

不过需要注意的时,分页查询的目标是尽快的返回前N条记录,因此,无论是ROWNUM还是FIRST_ROWS机制都是提高前几页的查询速度,对于分页查询的最后几页,采用HASH JOIN的方式,执行效率几乎没有任何改变,而采用NESTED LOOP方式,则效率严重下降,而且远远低于HASH JOIN的方式。

四、排序的列不唯一

当用来排序的列存在值相等的行,可能会造成数据重复出现,因为Oracle的排序算法不具有稳定性,即键值相等的数据A和B,在排序之前A在B的前面,在排序之后A不一定在B的前面了(排序算法的稳定性评判标准)。

解决办法:

  1. 排序的时候,除了需要字段,跟一个有索引的字段,例如主键;
  2. 跟ROWID也可以,这种方法最简单,且对性能的影响最小。

参考链接1
参考链接2

猜你喜欢

转载自blog.csdn.net/weixin_43525116/article/details/85006795