现在的位置: 首页 > 综合 > 正文

Oracle9i新增WITH语法

2018年02月01日 ⁄ 综合 ⁄ 共 3236字 ⁄ 字号 评论关闭

 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数量。

可惜的是,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 关键字

 

 

========================================================================

原来,UPDATE可以这样写!

在讨论一个有关表的UPDATE时, 写了如下的SQL:<原始需求,请参考:http://www.oracle.com.cn/viewthr ... ghlight=&page=2>

SQL> update test2 set spc = (
  2         select substr(max(sys_connect_by_path(b.name, '-')),2) name
  3             from (select rn, skycode id,
  4                          decode(rn, 1, 0, instr(skycode, '-', 1, rn-1)) + 1 sp,
  5                          decode(instr(skycode, '-', 1, rn), 0, length(skycode) + 1, instr(skycode, '-', 1, rn)) ep
  6                      from (select rownum rn from dual connect by rownum<=20) a, test2 b
  7                      where decode(rn, 1, 0, instr(skycode, '-', 1, rn-1)) + instr(skycode, '-', 1, rn) > 0
  8                  ) a, test b
  9             where b.citycode = substr(a.id, sp, ep-sp) and a.id=test2.skycode
10             start with rn=1 connect by rn-1=prior rn and a.id = prior a.id );

当时是在9.2.0.7下做的,没有问题,可以有朋友在9.2.0.1下,就会出现:

ORA-03113:通信通道文件结束
ORA-03114:未连接ORACLE

的错误提示, 这是9.2.0.1的一个BUG, 在多次的子查询时出现, 我试过,在9.2.0.5已经没有了,但不知道从那个版本ORACLE做了更正.

前段时间在写类似的多子查询的SELECT语句时, ORACLE9I提供的一个新子句: WITH在某种程度上解决了部分这类错误的出现. 经测试,原来同样的写法,也可以用于UPDATE中, 如上面的语句,可以用WITH改写为:

SQL> update test2 set spc = (
  2         with myque as (select rn, skycode id,
  3                          decode(rn, 1, 0, instr(skycode, '-', 1, rn-1)) + 1 sp,
  4                          decode(instr(skycode, '-', 1, rn), 0, length(skycode) + 1,
  5                                                                instr(skycode, '-', 1, rn)) ep
  6                       from (select rownum rn from dual connect by rownum<=20) a, test2 b
  7                       where decode(rn, 1, 0, instr(skycode, '-', 1, rn-1)) + instr(skycode, '-', 1, rn) > 0 )
  8         select substr(max(sys_connect_by_path(b.name, '-')),2) name
  9            from myque a, test b
10            where b.citycode = substr(a.id, sp, ep-sp) and a.id=test2.skycode
11            start with rn=1 connect by rn-1=prior rn and a.id = prior a.id );

已更新4行。

可见, ORACLE在支持子查询的地方,同时也支持WITH的操作, 本人认为,这样一来,可以让开发人员有更多的机会,写出高效的单个SQL语句. 特别是在多个子查询中多次对同一基表进行访问时.

抱歉!评论已关闭.