服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > 数据库技术 > Oracle技术 > DBA > 查看文档

oracle 9i 数据库with语法小议

  oracle9i新增了with语法功能,可以将查询中的子查询命名,放到select语句的最前面。

  oracle9i新增了with语法功能,可以将查询中的子查询命名,放到select语句的最前面。

  下面看一个简单的例子:

sql> with
2 seg as (select segment_name, sum(bytes)/1024 k from user_segments group by segment_name),
3 obj as (select object_name, object_type from user_objects)
4 select o.object_name, object_type, nvl(s.k, 0) size_k
5 from obj o, seg s
6 where o.object_name = s.segment_name (+)
7 ;
object_name object_type size_k
------------------------------ ------------------- ----------
daijc_test table 128
p_test procedure 0
ind_daijc_test_c1 index 128

  通过with语句定义了两个子查询seg和obj,在随后的select语句中可以直接对预定义的子查询进行查询。从上面的例子也可以看出,使用with语句,将一个包含聚集、外连接等操作sql清晰的展现出来。

  with定义的子查询不仅可以使查询语句更加简单、清晰,而且with定义的子查询还具有在select语句的任意层均可见的特点。

  即使是在with的定义层中,后定义的子查询都可以使用前面已经定义好的子查询:

sql> with
2 q1 as (select 3 + 5 s from dual),
3 q2 as (select 3 * 5 m from dual),
4 q3 as (select s, m, s + m, s * m from q1, q2)
5 select * from q3;
s m s+m s*m
---------- ---------- ---------- ----------
8 15 23 120

  利用with定义查询中出现多次的子查询还能带来性能提示。oracle会对with进行性能优化,当需要多次访问with定义的子查询时,oracle会将子查询的结果放到一个临时表中,避免同样的子查询多次执行,从而有效的减少了查询的io数量。

  看一个简单的例子,首先构造一张大表,现在要取出大表中id最小、id最大以及id等于平均值的记录,看看普通写法和with语句的区别:

sql> create table t_with as select rownum id, a.* from dba_source a where rownum < 100001;

  表已创建。

sql> set timing on
sql> set autot on
sql> select id, name from t_with
2 where id in
3 (
4 select max(id) from t_with
5 union all
6 select min(id) from t_with
7 union all
8 select trunc(avg(id)) from t_with
9 );

id name
---------- ------------------------------
1 standard
50000 dbms_backup_restore
100000 initjvmaux

已用时间: 00: 00: 00.09

执行计划
----------------------------------------------------------
plan hash value: 647530712

-----------------------------------------------------------
| id | operation | name | rows | bytes |
-----------------------------------------------------------
| 0 | select statement | | 3 | 129 |
|* 1 | hash join | | 3 | 129 |
| 2 | view | vw_nso_1 | 3 | 39 |
| 3 | hash unique | | 3 | 39 |
| 4 | union-all | | | |
| 5 | sort aggregate | | 1 | 13 |
| 6 | table access full| t_with | 112k| 1429k|
| 7 | sort aggregate | | 1 | 13 |
| 8 | table access full| t_with | 112k| 1429k|
| 9 | sort aggregate | | 1 | 13 |
| 10 | table access full| t_with | 112k| 1429k|
| 11 | table access full | t_with | 112k| 3299k|
-----------------------------------------------------------

predicate information (identified by operation id):
---------------------------------------------------

1 - access("id"="$nso_col_1")

note
-----
- dynamic sampling used for this statement

统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
5529 consistent gets
0 physical reads
0 redo size
543 bytes sent via sql*net to client
385 bytes received via sql*net from client
2 sql*net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3 rows processed

  为了避免第一次执行时物理读的影响,查询结果选取了sql的第三次运行,物理读为0时的统计信息。

  观察执行计划可以看到,先后对t_with表进行了4次全表扫描,并产生了5529个逻辑读。下面看看with语句的表现:

sql> with
2 agg as (select max(id) max, min(id) min, trunc(avg(id)) avg from t_with)
3 select id, name from t_with
4 where id in
5 (
6 select max from agg
7 union all
8 select min from agg
9 union all
10 select avg from agg
11 );

id name
---------- ------------------------------
1 standard
50000 dbms_backup_restore
100000 initjvmaux

已用时间: 00: 00: 00.07

执行计划
----------------------------------------------------------
plan hash value: 1033356310

----------------------------------------------------------------------------------
| id | operation | name | rows | bytes |
----------------------------------------------------------------------------------
| 0 | select statement | | 3 | 129 |
| 1 | temp table transformation | | | |
| 2 | load as select | t_with | | |
| 3 | sort aggregate | | 1 | 13 |
| 4 | table access full | t_with | 112k| 1429k|
|* 5 | hash join | | 3 | 129 |
| 6 | view | vw_nso_1 | 3 | 39 |
| 7 | hash unique | | 3 | 39 |
| 8 | union-all | | | |
| 9 | view | | 1 | 13 |
| 10 | table access full | sys_temp_0fd9d662e_bf2edf12 | 1 | 13 |
| 11 | view | | 1 | 13 |
| 12 | table access full | sys_temp_0fd9d662e_bf2edf12 | 1 | 13 |
| 13 | view | | 1 | 13 |
| 14 | table access full | sys_temp_0fd9d662e_bf2edf12 | 1 | 13 |
| 15 | table access full | t_with | 112k| 3299k|
----------------------------------------------------------------------------------

predicate information (identified by operation id):
---------------------------------------------------

5 - access("id"="$nso_col_1")

note
-----
- dynamic sampling used for this statement

统计信息
----------------------------------------------------------
2 recursive calls
8 db block gets
2776 consistent gets
1 physical reads
648 redo size
543 bytes sent via sql*net to client
385 bytes received via sql*net from client
2 sql*net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3 rows processed

  观察这次的执行计划,发现只对t_with表进行了两次全表扫描,而从逻辑读上也可以观察到,这次只产生了2776的逻辑读,正好是上面不使用with语句的一半。

  通过分析执行计划,oracle执行了with子查询一次,并将结果放到了临时表中,在随后对子查询的多次访问中,都从临时表中直接读取了数据,这应该也是那1个物理读的由来。

  通过上面的例子可以看到,将子查询放到with语句中不仅可以简化查询语句的结构,对于子查询需要多次执行的情况,还有可能提示查询的性能。

  可惜的是,with语句只能用在select语句中,update和delete语句不支持with语法:

sql> set autot off
sql> set timing off
sql> with subq as (select 1 from dual)
2 select id, name from t_with where id in (select * from subq);

id name
---------- ------------------------------
1 standard

sql> with subq as (select 1 from dual)
2 update t_with set id = 1 where id in (select * from subq);
update t_with set id = 1 where id in (select * from subq)
*第 2 行出现错误:
ora-00928: 缺失 select 关键字


sql> with subq as (select 1 from dual)
2 delete t_with where id in (select * from subq);
delete t_with where id in (select * from subq)

*第 2 行出现错误:
ora-00928: 缺失 select 关键字

  

扫描关注微信公众号