当数据量太大,例如咱们数据库的一个表里,符合查询条件的有几十万条数据,如果你一次性查询出来的话,不仅查询速度很慢,响应时间太长影响用户体验,而且用户也看不了这么多数据。
可能有人会说可以在后台进行分页类封装处理(逻辑分页
),那你首先要把查询到的几十万条数据存在内存里,这样看来,还是推荐数据库分页查询(物理分页
)吧
一、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,在第一层的时候就将数据过滤好了,再将量减少了的数据向外返回,是不是效率自然就高了起来。
注意:上述两种格式,针对最内层是复杂多表联合的查询,也同样适用。
UNION
、UNION ALL
、MINUS
、INTERSECT
、GROUP BY
、DISTINCT
、UNIQUE
以及聚集函数如MAX
、MIN
和分析函数
等操作是针对,全部符合条件的数据的结果集,的一个操作,所以如果最内层的子查询中包含了这些操作中的一个以上,那么ROWNUM
会不起作用,分页查询的性能等于没实现。
Oracle10g的新功能GROUP BY STOPKEY
,使得Oracle10g解决了GROUP BY操作分页效率低的问题。在10g以前,Oracle的GROUP BY操作必须完全执行完,才能将结果返回给用户。但是Oracle10g增加了GROUP BY STOPKEY
执行路径,使得用户在执行GROUP BY
操作时,可以根据STOPKEY
随时中止正在运行的操作。这使得标准分页函数对于GROUP BY
操作重新发挥了作用。
二、ROWNUM
操作执行原理
- 执行查询操作;
- 将第一行的ROWNUM置为1;
- 将得到的行的ROWNUM与条件相比较,如果不匹配,则抛弃行,如果匹配,则返回行;
- oracle获取下一行,然后将ROWNUM增1;
- 返回第3步;
- 直到超过ROWNUM的限制条件;
ROWNUM 使用注意事项
- ROWNUM不能以任何基表的名称作为前缀。
- 子查询中的ROWNUM必须要有别名,否则还是不会查出记录来,这是因为ROWNUM不是某个表的列,如果不起别名的话,无法知道ROWNUM是子查询的列还是主查询的列。
- 查询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的前面了(排序算法的稳定性评判标准)。
解决办法:
- 排序的时候,除了需要字段,跟一个有索引的字段,例如主键;
- 跟ROWID也可以,这种方法最简单,且对性能的影响最小。