基于UNION ALL的分页查询执行计划问题(二)

简介: 今天又发现9204上的一个问题。不过这个问题并不会造成数据的错误,但是会严重的影响查询的性能。基于UNION ALL的分页查询执行计划问题:http://yangtingkun.itpub.net/post/468/303221这篇文章继续讨论这个问题,并给出一个相对简单的解决方法。

今天又发现9204上的一个问题。不过这个问题并不会造成数据的错误,但是会严重的影响查询的性能。

基于UNION ALL的分页查询执行计划问题:http://yangtingkun.itpub.net/post/468/303221

这篇文章继续讨论这个问题,并给出一个相对简单的解决方法。


首先发现的第一个问题是,这个问题是由UNION ALL引起的,而和视图没有关系。

SQL> SELECT *
2 FROM
3 (
4 SELECT ROWNUM RN, A.*
5 FROM
6 (
7 SELECT /*+ FIRST_ROWS */ * FROM V_T
8 WHERE CREATE_DATE = SYSDATE - 2
9 ) A
10 WHERE ROWNUM <= 10
11 )
12 WHERE RN > 1
13 ;

未选定行

执行计划
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=11 Card=10 Bytes=520)
1 0 VIEW (Cost=11 Card=10 Bytes=520)
2 1 COUNT (STOPKEY)
3 2 VIEW OF 'V_T' (Cost=11 Card=14885 Bytes=580515)
4 3 UNION-ALL
5 4 TABLE ACCESS (FULL) OF 'T1' (Cost=9 Card=13727 Bytes=480445)
6 4 TABLE ACCESS (FULL) OF 'T2' (Cost=2 Card=1158 Bytes=35898)

SQL> SELECT *
2 FROM
3 (
4 SELECT ROWNUM RN, A.*
5 FROM
6 (
7 SELECT /*+ FIRST_ROWS */ * FROM
8 (
9 SELECT * FROM T1
10 UNION ALL
11 SELECT * FROM T2
12 )
13 WHERE CREATE_DATE = SYSDATE - 2
14 ) A
15 WHERE ROWNUM <= 10
16 )
17 WHERE RN > 1
18 ;

未选定行

执行计划
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=11 Card=10 Bytes=520)
1 0 VIEW (Cost=11 Card=10 Bytes=520)
2 1 COUNT (STOPKEY)
3 2 VIEW (Cost=11 Card=14885 Bytes=580515)
4 3 UNION-ALL
5 4 TABLE ACCESS (FULL) OF 'T1' (Cost=9 Card=13727 Bytes=480445)
6 4 TABLE ACCESS (FULL) OF 'T2' (Cost=2 Card=1158 Bytes=35898)

Metalink上搜索了一下,没有找到这个问题的描述,不过似乎和下面这个问题有几分类似:Note:2281909.8

Description

Suboptimal plan possible from INLINE non-correlated UNION ALL subquery. When this problem occurs the execution plan indicates that the subquery has been unnested to a view, and a join predicate was pushed into the view.

这个bug中的问题是由于Oracle错误的将连接列的查询条件推入到UNION ALL子查询中,导致性能下降。而当前的问题是Oracle没有把限制条件推入到UNION ALL子查询中去。

对于这个问题的解决,就是避免在ROWNUM出现后,在外层再嵌套一层查询。

当然升级到10g也是一种选择,不过代价比较大。

对于分页操作由于无法避免三层嵌套查询,可以利用MINUS来解决这个问题:

SQL> SELECT ROWNUM, A.*
2 FROM
3 (
4 SELECT /*+ FIRST_ROWS */ * FROM V_T
5 WHERE CREATE_DATE = SYSDATE - 2
6 ORDER BY NAME
7 ) A
8 WHERE ROWNUM <= 20
9 MINUS
10 SELECT ROWNUM, A.*
11 FROM
12 (
13 SELECT /*+ FIRST_ROWS */ * FROM V_T
14 WHERE CREATE_DATE = SYSDATE - 2
15 ORDER BY NAME
16 ) A
17 WHERE ROWNUM <= 10
18 ;

未选定行

执行计划
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=18 Card=20 Bytes=1170)
1 0 MINUS
2 1 SORT (UNIQUE) (Cost=9 Card=20 Bytes=780)
3 2 COUNT (STOPKEY)
4 3 VIEW (Cost=7 Card=21 Bytes=819)
5 4 SORT (ORDER BY STOPKEY) (Cost=7 Card=21 Bytes=777)
6 5 VIEW OF 'V_T' (Cost=4 Card=21 Bytes=777)
7 6 UNION-ALL (PARTITION)
8 7 TABLE ACCESS (BY INDEX ROWID) OF 'T1' (Cost=4 Card=35 Bytes=1225)
9 8 INDEX (RANGE SCAN) OF 'IND_T1_CREATE_DATE' (NON-UNIQUE) (Cost=1 Card=35)
10 7 TABLE ACCESS (BY INDEX ROWID) OF 'T2' (Cost=3 Card=2 Bytes=62)
11 10 INDEX (RANGE SCAN) OF 'IND_T2_CREATE_DATE' (NON-UNIQUE) (Cost=1 Card=2)
12 1 SORT (UNIQUE) (Cost=9 Card=10 Bytes=390)
13 12 COUNT (STOPKEY)
14 13 VIEW (Cost=7 Card=21 Bytes=819)
15 14 SORT (ORDER BY STOPKEY) (Cost=7 Card=21 Bytes=777)
16 15 VIEW OF 'V_T' (Cost=4 Card=21 Bytes=777)
17 16 UNION-ALL (PARTITION)
18 17 TABLE ACCESS (BY INDEX ROWID) OF 'T1' (Cost=4 Card=35 Bytes=1225)
19 18 INDEX (RANGE SCAN) OF 'IND_T1_CREATE_DATE' (NON-UNIQUE) (Cost=1 Card=35)
20 17 TABLE ACCESS (BY INDEX ROWID) OF 'T2' (Cost=3 Card=2 Bytes=62)
21 20 INDEX (RANGE SCAN) OF 'IND_T2_CREATE_DATE' (NON-UNIQUE) (Cost=1 Card=2)

采用这种方式,可以在利用索引的基础上完成翻页的功能,不过这种方法对于结果集靠后的记录可能会导致查询时间成倍增加。

使用这个方法一般只在下面两个条件都成立:

已经碰到了基于UNION ALL的查询不走索引的情况;

索引查询的选择度比较高,能够确保过滤掉绝大部分的数据。

相关文章
|
12天前
|
SQL 关系型数据库 MySQL
【MySQL】:分组查询、排序查询、分页查询、以及执行顺序
【MySQL】:分组查询、排序查询、分页查询、以及执行顺序
15 0
|
4月前
|
SQL
SQL-条件查询与聚合函数的使用
SQL-条件查询与聚合函数的使用
|
5月前
|
SQL 算法 关系型数据库
MySQL查询优化之order by 、 group by与分页查询优化
MySQL查询优化之order by 、 group by与分页查询优化
60 0
|
5月前
|
存储 关系型数据库 MySQL
MySQL查询执行计划详解(EXPLAIN)
一、单表查询 访问方法/访问类型: • const:通过主键值或唯一二级索引与一个常熟进行等值查询(不包括NULL),只会生成一条记录 • ref:普通二级索引与一个常数进行等值比较,可能生成多条记录 • ref_or_null:ref的前提下可以加上or key is null • range:对应的扫描区间为若干个单点扫描区间或范围扫描区间(不包括负无穷到正无穷的范围) • index:扫描区间为全表,但是可以在二级索引中扫描(因为二级索引每条记录占用空间更小,所以需要读的页更少) • all:直接扫描全部的聚集索引记录
|
12月前
|
SQL
explain分析查询
explain分析查询
49 0
|
Oracle 关系型数据库 MySQL
Mysql数据库,子查询,union,limit篇
参数 expression1, expression2, ... expression_n: 要检索的列。 tables: 要检索的数据表。 WHERE conditions: 可选, 检索条件。 DISTINCT: 可选,删除结果集中重复的数据。默认情况下 UNION 操作符已经删除了重复数据,所以 DISTINCT修饰符对结果没啥影响。 ALL: 可选,返回所有结果集,包含重复数据。 案例:查询工作岗位为MANAGER或者SALESMAN的员工信息(使用union)
118 0
|
SQL Oracle 关系型数据库
SQL 连接查询、子查询、union
SQL 连接查询、子查询、union
224 0
SQL 连接查询、子查询、union
|
SQL 程序员 数据库
|
关系型数据库 MySQL
MySQL查询——select
MySQL查询——selectSELECT  select的完整语法: 复制代码select col1, col2,... # 业务查询的字段from table_name # 选取的哪张表[where single_conditions] ...
2673 0
|
存储 关系型数据库 测试技术