视图view是我们在开发中经常使用的数据库对象。通过视图,我们可以将复杂查询逻辑屏蔽,向上提供更具有业务含义的数据访问接口。
本质上说,视图是一个SQL语句构成的虚拟表。我们对视图的查询就相当于对SQL语句形成数据集合的再次查询。当我们需要提高视图查询性能的时候,是从什么地方着手呢?
索引可能是我们最直接的想法。借助查询索引,可以为Oracle优化器提供一个可供选择的优化路径。但是,索引是一个对象,一般设置在有实际数据的数据表上,而视图本身没有任何数据(除物化视图之外)。对普通视图列加索引是不可以的。
SQL> create or replace view v_t1 as
2 select *
3 from t;
View created
SQL> create index idx_v_t1_from on v_t1(SERIES_FROM);
create index idx_v_t1_from on v_t1(SERIES_FROM)
ORA-01702: 视图不适用于此处
那么,是不是说使用视图就无法进行性能方面的优化了吗?答案是否定的,我们可以从视图使用的基表入手,设置优化策略。下面我们根据视图使用的几个场景分别进行分析。
环境构建
建立数据表t,结构如下:
SQL> create table t
2 ( seq_number number(13),
3 series_from varchar2(10),
4 series_to varchar2(10),
5 comm varchar2(20));
Table created
SQL> desc t;
Name Type Nullable Default Comments
----------- ------------ -------- ------- --------
SEQ_NUMBER NUMBER(13)
SERIES_FROM VARCHAR2(10)
SERIES_TO VARCHAR2(10)
COMM VARCHAR2(20)
//在series from 与series to列建立唯一索引(唯一约束是业务要求)
SQL> create unique index idx_t_from on t(series_from);
Index created
SQL> create unique index idx_t_to on t(series_to);
Index created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
//之后,借助pl/sql脚本生成测试数据,与本议题无关,略过。
单数据表查询
业务场景:根据一个票号(10位长度)搜索出对应的票号段。
实验SQL:select * fromt where series_fromlpad('1000',10,'0');
在不使用视图的情况下,我们查询号段。
SQL> select * from scott.t where series_fromlpad('1000',10,'0');
已用时间: 00: 00: 00.14
执行计划
----------------------------------------------------------
Plan hash value: 1600714554
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 476 | 2 (0)|00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| T | 17 | 476 | 2 (0)|00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_T_FROM | 18 | | 1 (0)|00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SERIES_TO">'0000001000')
2 - access("SERIES_FROM"
统计信息
----------------------------------------------------------
1388 recursive calls
0 db block gets
275 consistent gets
0 physical reads
0 redo size
524 bytes sent via SQL*Net to client
377 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
28 sorts (memory)
0 sorts (disk)
1 rows processed
发现,当我们直接对表使用查询的时候,优化器选择了索引作为执行路径。
同名视图查询
首先,我们测试最简单的一种视图形式。视图对应一个数据表,而且视图列名与数据表名相同。
SQL> create or replace view v_t1 as
2 select *
3 from t;
View created
进行相同查询。
SQL> select * from scott.v_t1 where series_fromlpad('1000',10,'0');
已用时间: 00: 00: 00.14
执行计划
----------------------------------------------------------
Plan hash value: 1600714554
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 476 | 2 (0)|00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| T | 17 | 476 | 2 (0)|00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_T_FROM | 18 | | 1 (0)|00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("SERIES_TO">'0000001000')
2 - access("SERIES_FROM"
统计信息
----------------------------------------------------------
1596 recursive calls
0 db block gets
319 consistent gets
0 physical reads
0 redo size
524 bytes sent via SQL*Net to client
377 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
36 sorts (memory)
0 sorts (disk)
1 rows processed
发现,虽然使用了视图v_t1作为查询对象,但是产生的执行计划、成本消耗和访问数据路径与对数据表t进行查询的结果一样。而且执行时间都是一样。
所以,在单表同列名视图的情况下,我们对视图的查询与数据表相同。对视图施加的条件可以完全映射到数据表上,并且应用适当的索引优化路径。
单表异名视图查询
那么,上面的情况是不是因为视图列表与数据表列名恰好相同。如果在定义视图的时候使用了一系列的as别名,查询视图的时候使用别名进行查询,会不会有同样的结论呢?
SQL> create or replace view v_t2 as
2 select t.seq_number as a, t.series_from as b, t.series_to as c, t.comm as d
3 from t;
View created
SQL> select * from scott.v_t2 where blpad('1000',10,'0');
已用时间: 00: 00: 00.03
执行计划
----------------------------------------------------------
Plan hash value: 1600714554
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 17 | 476 | 2 (0)|00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| T | 17 | 476 | 2 (0)|00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_T_FROM | 18 | | 1 (0)|00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("T"."SERIES_TO">'0000001000')
2 - access("T"."SERIES_FROM"
统计信息
----------------------------------------------------------
688 recursive calls
0 db block gets
146 consistent gets
0 physical reads
0 redo size
494 bytes sent via SQL*Net to client
377 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
13 sorts (memory)
0 sorts (disk)
1 rows processed
结论显而易见,与上面的结果相同。执行计划相同,路径一致。而且值得关注的是,将对应视图的查询条件完整的映射为数据基表的列名。
Union多结果集合异列名
最后,我们一起看看使用union的视图 。union相当于将多个数据表数据源的对象集合合并处理。由于对应数据表不同,常常使用异列名情况,对应的逻辑也往往比较复杂。
SQL> create or replace view v_t3 as
2 select t.seq_number as a, t.series_from as b, t.series_to as c, t.comm as d
3 from t
4 union all
5 select t1.seq_number as a, t1.series_from as b, t1.series_to as c, t1.comm as d
6 from t1
7 with check option;
View created
其中,数据表t1是一张与t相同结构的数据表。应用相同的查询后,我们看结果。
SQL> select * from scott.v_t3 where blpad('1000',10,'0');
已用时间: 00: 00: 00.08
执行计划
----------------------------------------------------------
Plan hash value: 2234649797
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 18 | 702 | 6 (34)| 00:00:01 |
| 1 | VIEW | V_T3 | 18 | 702 | 6 (34)| 00:00:01 |
| 2 | SORT UNIQUE | | 18 | 504 | 6 (67)| 00:00:01 |
| 3 | UNION-ALL | | | | |
|* 4 | TABLE ACCESS BY INDEX ROWID| T | 17 | 476 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_T_FROM | 18 | | 1 (0)| 00:00:01 |
|* 6 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 28 | 2 (0)| 00:00:01 |
|* 7 | INDEX RANGE SCAN | IDX_T1_FROM | 1 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("T"."SERIES_TO">'0000001000')
5 - access("T"."SERIES_FROM"
6 - filter("T1"."SERIES_TO">'0000001000')
7 - access("T1"."SERIES_FROM"
统计信息
----------------------------------------------------------
8 recursive calls
0 db block gets
5 consistent gets
25 physical reads
0 redo size
494 bytes sent via SQL*Net to client
377 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed
结论也很明显。在union的情况下,Oracle将对视图的查询条件根据列名映射规则,转为union多个数据集合的额外查询条件。并且分别应用上了数据表的索引路径。
有一点需要说明的是,这种情况下进行搜索的时间可能略长。主要是因为多个数据表都要进行查询和结果集合合并。这种情况一般也是业务方面能够允许的损耗。
最后,我们说的视图不能加索引,是仅限于普通视图。如果是带有数据信息的物化视图,是可以建立索引的。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/17203031/viewspace-684157/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/17203031/viewspace-684157/