[转帖]介绍Oracle的with语法_MySQL, Oracle及数据库讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  MySQL, Oracle及数据库讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3607 | 回复: 0   主题: [转帖]介绍Oracle的with语法        下一篇 
kim
注册用户
等级:中校
经验:1729
发帖:222
精华:0
注册:2011-7-21
状态:离线
发送短消息息给kim 加好友    发送短消息息给kim 发消息
发表于: IP:您无权察看 2011-8-11 10:10:54 | [全部帖] [楼主帖] 楼主

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 关键字




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论