查看: 13174|回复: 11

[精华] “子查询定义”从句总结(WITH AS 语句)

[复制链接]
论坛徽章:
16
数据库板块每日发贴之星
日期:2007-12-03 01:03:272010世博会纪念徽章
日期:2010-09-14 17:24:312010新春纪念徽章
日期:2010-03-01 11:20:05生肖徽章2007版:马
日期:2009-11-13 10:55:15参与WIN7挑战赛纪念
日期:2009-11-06 10:44:24CTO参与奖
日期:2009-03-23 11:00:18生肖徽章2007版:马
日期:2009-02-08 17:20:54生肖徽章2007版:兔
日期:2008-12-01 13:33:59生肖徽章2007版:马
日期:2008-10-31 19:01:49奥运会纪念徽章:赛艇
日期:2008-10-24 13:25:17
跳转到指定楼层
1#
发表于 2009-11-12 20:34 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
“子查询定义”从句总结(WITH AS 语句)

一、先看下oracle 10g SQL参考文档(Ora 10G SQL Reference)中的描述(以下汉译部分,仅是个人理解,仅供参考)
1“子查询定义”从句 定义格式:
WITH query_name AS (subquery)     [, query_name AS (subquery) ]...    2subquery_factoring_clause (“子查询定义”从句部分)
The WITH query_name clause lets you assign a name to a subquery block. You can then reference the subquery block multiple places in the query by specifying the query name. Oracle Database optimizes the query by treating the query name as either an inline view or as a temporary table.

(使用WITH AS 语句可以为一个子查询语句块定义一个名称,使用这个子查询名称可以在查询语句的很多地方引用这个子查询。Oracle数据库像对待内联视图或临时表一样对待被引用的子查询名称,从而起到一定的优化作用。)

You can specify this clause in any top-level SELECT statement and in most types of subqueries. The query name is visible to the main query and to all subsequent subqueries except the subquery that defines the query name itself.

(你可以在任何一个顶层的SELECT语句以及几乎所有类型的子查询语句前,使用子查询定义子句。被定义的子查询名称可以在主查询语句以及所有的子查询语句中引用,但未定义前不能引用

3Restrictions on Subquery Factoring (“子查询定义”从句的限制性)

·         You cannot nest this clause. That is, you cannot specify the subquery_factoring_clause within the subquery of another subquery_factoring_clause. However, a query_name defined in one subquery_factoring_clause can be used in the subquery of any subsequent subquery_factoring_clause.

(不能嵌套定义,但子查询中出现的“子查询定义”语句可以引用已定义的子查询名称。)

·         In a query with set operators, the set operator subquery cannot contain the subquery_factoring_clause, but the FROM subquery can contain the subquery_factoring_clause.

(在带集合操作的查询语句中,除FROM子查询外,子查询中不能出现“子查询定义”语句。但通过测试后感觉并不是这样,详见后面的示例。不知道是不是对此段英文理解错误。)

二、        个人总结及示例:

1、浅谈该功能的oracle 原理:先对子查询起个名称,然后可以多处引用,并像内联视图或临时表一样对待,从而减少访问基表的次数,提高运行效率。

2、使用时机,一般在需要多次访问一个子查询结果集的情况下使用。注意oracle数据库版本的限制,该语句是在9i及以后数据库版本中才出现的。

3、使用原则及相关示例:

1> 定义之后一定要引用
SQL> select * from  (
  2 WITH t_with_3 as (SELECT '1' a ,'3' b FROM DUAL)
  3  SELECT '1' a ,'1' b FROM dual -- 此处未引用
  4  )
  5  /
WITH t_with_3 as (SELECT '1' a ,'3' b FROM DUAL)
                                           *
ERROR 位于第2行:
ORA-32035: unreferenced query name defined in WITH clause

2>、同一个主查询语句的同级别的地方,子查询定义语句只能出现一个,而不能并列出现多个
1
SQL>  SELECT  *
  2    FROM (
  3          WITH
  4            t_with_1 AS (SELECT '1' a ,'1' b FROM DUAL),
  5          WITH  -- 此处再次出现定义
  6            t_with_2 AS (SELECT '1' a ,'2' b FROM DUAL)
  7*         SELECT x.a , x.b xb ,y.b yb FROM t_with_1 x , t_with_2 y
  8  /
        WITH
        *
ERROR 位于第5行:
ORA-00903: invalid table name

2:带集合操作的查询

SQL>  SELECT  *
  2    FROM (
  3          WITH
  4            t_with_1 AS (SELECT '1' a ,'1' b FROM DUAL)
  5          --此后的整个语句被认为是一个主查询语句
  6          SELECT x.a , x.b FROM t_with_1 x
  7          union all
  8          WITH  -- 此处再次出现定义
  9            t_with_2 AS (SELECT '1' a ,'2' b FROM DUAL)
10*         SELECT y.a ,y.b FROM t_with_2 y
11  /
        SELECT y.a ,y.b FROM t_with_2 y
        *
ERROR 位于第10行:
ORA-32034: unsupported use of WITH clause

3>、不能嵌套定义
SQL>  SELECT  *
  2    FROM (
  3          WITH
  4            t_with_1 AS (  select * from (
  5                                          WITH t_with_x as (SELECT '1' a ,'x' b FROM DUAL)
  6                                          SELECT '1' a ,'1' b FROM t_with_x
  7                                          )
  8                         ),
  9            t_with_2 AS (SELECT '1' a ,'2' b FROM DUAL)
10*         SELECT x.a , x.b xb ,y.b yb FROM t_with_1 x , t_with_2 y
11  /
                                        WITH t_with_x as (SELECT '1' a ,'x' b FROM DUAL)
                                        *
ERROR 位于第5行:
ORA-32034: unsupported use of WITH clause

4>、可"嵌套"引用(未定义之前不能引用)
SQL>  SELECT  *
  2    FROM (
  3          WITH
  4            t_with_1 AS (SELECT '1' a ,'1' b FROM DUAL),
  5            t_with_2 AS (SELECT '1' a ,'2' b FROM DUAL),
  6            t_with_3 as (SELECT *  FROM t_with_1)
  7          SELECT x.a , x.b xb ,y.b yb,z.b zb FROM t_with_1 x , t_with_2 y ,t_with_3 z
  8*         )
  9  /

A X Y Z
- - - -
1 1 2 1

5>、可在大多数类型的子查询中引用
SQL>  SELECT a ,b
  2    FROM (
3          WITH
  4            t_with AS (SELECT '1' a FROM DUAL)
  5          SELECT x.a ,(select a from t_with) b
  6            FROM t_with x,
  7                 (select a from t_with) y
  8           WHERE x.a= y.a
  9              and x.a = (select a from t_with)
10*         )
11  /

A B
- -
1 1

6>、可在大多数类型的子查询中定义及引用
1
SQL>  SELECT a ,b
  2    FROM (
  3          WITH
  4            t_with AS (SELECT '1' a FROM DUAL)
  5          SELECT x.a ,(
  6                        WITH t_with_z as (SELECT '1' a FROM t_with )
  7                        SELECT  s_1.a FROM t_with_z s_1 ,t_with s_2
  8                       ) b
  9            FROM t_with x,
10                 (  WITH t_with_y as (SELECT '1' a FROM t_with)
11                    SELECT  f_1.a FROM t_with_y f_1, t_with f_2
12                 ) y
13           WHERE x.a= y.a
14              and x.a = ( WITH t_with_x as (SELECT '1' a FROM t_with)
15                          SELECT  w_1.a FROM t_with_x w_1 ,t_with w_2
16                        )
17*        )
18  /

A B
- -
1 1

2:带集合操作的查询 注意并非FROM子查询中才能使用“子查询定义”从句)
SQL>  SELECT  *
  2    FROM (
  3          WITH
  4              t_with_1 AS (SELECT '1' a ,'1' b FROM DUAL)
  5          SELECT x.a , x.b ,'x' c FROM t_with_1 x
  6          union all
  7          SELECT y.a ,y.b ,  (WITH
  8                                  t_with_x AS (SELECT * FROM t_with_1)
  9                                  SELECT a FROM t_with_x )
10            FROM (  WITH
11                       t_with_2 AS (SELECT '1' a ,'2' b FROM DUAL)
12                    SELECT a ,b FROM t_with_2
13                                ) y
14          WHERE  y.a = (  WITH
15                              t_with_3 AS (SELECT '1' a ,'2' b FROM DUAL)
16                              SELECT a FROM t_with_3
17                       )
18*         )
19  /

A B C
- - -
1 1 x
1 2 1
论坛徽章:
519
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
2#
发表于 2009-11-12 22:46 | 只看该作者
老狐狸来给个精华?

In a query with set operators, the set operator subquery cannot contain the subquery_factoring_clause, but the FROM subquery can contain the subquery_factoring_clause.
这里说的是你例2的情况,你举的反例属于SCALAR SUBQUERY里面套用subquery_factoring_clause, 我原来还从未这么用过。

使用道具 举报

回复
论坛徽章:
16
数据库板块每日发贴之星
日期:2007-12-03 01:03:272010世博会纪念徽章
日期:2010-09-14 17:24:312010新春纪念徽章
日期:2010-03-01 11:20:05生肖徽章2007版:马
日期:2009-11-13 10:55:15参与WIN7挑战赛纪念
日期:2009-11-06 10:44:24CTO参与奖
日期:2009-03-23 11:00:18生肖徽章2007版:马
日期:2009-02-08 17:20:54生肖徽章2007版:兔
日期:2008-12-01 13:33:59生肖徽章2007版:马
日期:2008-10-31 19:01:49奥运会纪念徽章:赛艇
日期:2008-10-24 13:25:17
3#
 楼主| 发表于 2009-11-13 18:06 | 只看该作者
谢谢
newkid 老大的支持.

希望大家继续评论!

使用道具 举报

回复
论坛徽章:
519
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
4#
发表于 2009-11-14 00:14 | 只看该作者
希望楼主再研究一下11GR2的WITH用法。新版本的WITH允许递归使用,在功能上比原来有了质的飞跃,可以实现许多原来CONNECT BY做不到的事。

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
5#
发表于 2009-11-14 07:52 | 只看该作者
HEHE昨晚我也加精华并加亮显示的,看来这个帖子作者要继续努力了

使用道具 举报

回复
论坛徽章:
87
2015年新春福章
日期:2015-03-06 11:58:182010广州亚运会纪念徽章:轮滑
日期:2010-09-23 17:19:212010年世界杯参赛球队:乌拉圭
日期:2010-07-14 17:54:242010年世界杯参赛球队:美国
日期:2010-06-30 13:13:582010年世界杯参赛球队:墨西哥
日期:2010-06-25 12:49:452010年世界杯参赛球队:墨西哥
日期:2010-04-05 10:23:502010新春纪念徽章
日期:2010-03-01 11:06:232010新春纪念徽章
日期:2010-01-04 08:33:08生肖徽章2007版:龙
日期:2009-11-12 16:31:13参与WIN7挑战赛纪念
日期:2009-11-09 11:50:09
6#
发表于 2009-11-14 09:13 | 只看该作者
观摩中......

使用道具 举报

回复
论坛徽章:
16
数据库板块每日发贴之星
日期:2007-12-03 01:03:272010世博会纪念徽章
日期:2010-09-14 17:24:312010新春纪念徽章
日期:2010-03-01 11:20:05生肖徽章2007版:马
日期:2009-11-13 10:55:15参与WIN7挑战赛纪念
日期:2009-11-06 10:44:24CTO参与奖
日期:2009-03-23 11:00:18生肖徽章2007版:马
日期:2009-02-08 17:20:54生肖徽章2007版:兔
日期:2008-12-01 13:33:59生肖徽章2007版:马
日期:2008-10-31 19:01:49奥运会纪念徽章:赛艇
日期:2008-10-24 13:25:17
7#
 楼主| 发表于 2009-11-14 09:58 | 只看该作者
谢谢!
谢谢两位版主的大力支持!
如果以后还继续走oracle 这条路的话,我会更加努力!

使用道具 举报

回复
论坛徽章:
519
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
8#
发表于 2009-11-15 04:47 | 只看该作者
楼主再花点时间,研究一下INLINE VIEW转成WITH写法后,什么情况下会影响执行计划?是变好还是变差?
比如这贴:
http://www.itpub.net/thread-1234453-1-3.html

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
9#
发表于 2009-11-15 13:38 | 只看该作者

db2貌似2003年就实现了with as递归

从 Oracle 移植递归 SQL 到 DB2 UDB  

  

级别: 初级

Torsten Steinbach, IBM Germany


2003 年 7 月 01 日

首先本文描述了递归SQL如何工作以及它解决哪种类型的问题,然后解释了如何解决在使用递归 SQL从 Oracle 到 DB2 UDB 移植应用程序时可能发生的问题。
重要 :在阅读本文之前,请阅读 免责声明。

简介

递归 SQL 是用于查询数据层次结构的一种非常强大的方式。组织结构(部门、子部门、子子部门,等等)、讨论论坛(发贴、响应、对响应的响应,等等)、原料帐单、产品分类以及文档层次结构都是层次型数据的例子。

IBM® DB2® Universal Database™ (UDB)是实现了递归 SQL 的几种关系数据库产品中的一种。通常,可以将 DB2 方法看作一种高度强大和灵活的实现。DB2 在递归优势上的一个体现就是在单个的 DB2 表中查询多个层次结构的能力。(要了解更多这方面的细节,请参考在 DB2 开发者园地(DB2 Developer Domain)上由 Srini Venigalla 撰写的文章 使用 DB2 v7.2 中的 SQL UDF 扩大递归机会 。

如果您要将数据从一个 RDBMS 移植到另一个 RDBMS,那么重要的是要知道递归 SQL 的实现因产品而异。特别地,在 Oracle 与 DB2 UDB 之间的差异 这一部分,我将解释在将项目从 Oracle 移植到 DB2 并且涉及递归 SQL 时经常会出现的一个问题。

最根本的问题就是,在 Oracle 和 DB2 中,查询的默认排序次序各不相同。乍一看来这并不重要,因为通常应用程序并不十分依赖于默认的排序次序(没有使用 ORDER BY 子句)。然而在实际中,需要用 Oracle 提供的默认排序次序来解决许多问题,例如显示讨论的线索。很多应用程序都是基于 Oracle 的排序次序的假设,因而当要将那些应用程序移植到 DB2 UDB 时,要理解这一点。

当然,除了解释这个问题之外,我还会给出针对 DB2 中这一难题的解决方案的要点。要看这方面的内容,参见 在 DB2 UDB 中仿效 Oracle 的行为这一部分。

为了给读者提供有关一般递归,尤其是递归 SQL 的一些背景信息,我将从简要地介绍 DB2 递归 SQL 开始我们的话题。


递归 SQL 如何工作?

递归通常表现为三个基本的步骤:

1.初始化。
2.递归,或者在整个层次结构中重复对逻辑的迭代。
3.终止。
在初始步骤中,要准备好工作区域,并用初始值设置好变量。递归由工作区域中的商业逻辑操作以及随后对下一递归的调用组成,这里采用一种嵌套的方式。最后,终止步骤用于限定递归。打个比方,可以理解为对嵌套级数进行计数,当达到某一特定级数时便停止执行。

这一原理也可以应用到 DB2 中的递归 SQL。递归 SQL 是一种可以分为三个执行阶段的查询:

1.创建初始结果集。
2.基于现有的结果集进行递归。
3.查询完毕,返回最终的结果集。
初始的结果集建立在对基本表的常规 SQL 查询的基础上,这是公共表表达式(CTE)的第一部分。公共表表达式是用于支持递归的手段,它的第二部分对自己进行调用并将其与基本表相连接。从该 CTE 中进行选择的查询便是终止步骤。

下面的例子演示了这一过程。DEPARTMENT是一个包含了有关某个部门的信息的表:

CREATE TABLE departments (deptid INT,
                          deptname VARCHAR(20),
                          empcount INT,
                          superdept INT)



这个表的内容代表了一个层次结构。下面的 图 1就是一个例子:


图 1. 一个表层次结构的例子


对于一个给定的部门,该部门包括所有的子部门,要获得该部门的雇员人数,需要一个递归查询:

WITH temptab(deptid, empcount, superdept) AS
   (    SELECT root.deptid, root.empcount, root.superdept
            FROM departments root
            WHERE deptname='Production'
     UNION ALL
        SELECT sub.deptid, sub.empcount, sub.superdept
            FROM departments sub, temptab super
            WHERE sub.superdept = super.deptid
   )
SELECT sum(empcount) FROM temptab



在这个例子中,CTE 被称作 temptab,随着查询的继续执行,temptab 会逐渐变大。下面给出了所有的递归元素:

1.在 temptab 中建立初始结果集。它包含了部门“Production”的雇员人数:
SELECT root.deptid, root.empcount, root.superdept
            FROM departments root
            WHERE deptname='Production'



2.当在 temptab 中针对于各个子部门加入每一行记录时,便发生了递归。该递归每一次执行的结果都通过 UNION ALL 加入到 temptab 中:
SELECT sub.deptid, sub.empcount, sub.superdept
            FROM departments sub, temptab super
            WHERE sub.superdept = super.deptid



3.最后的查询就是从 CTE 中提取出所需的信息。在本例中,进行的是总计操作:
SELECT sum(empcount) FROM temptab



下面是例子查询的结果:

1
-----------
SQL0347W  The recursive common table expression "TORSTEN.TEMPTAB" may contain
an infinite loop.  SQLSTATE=01605
         50
  1 record(s) selected with 1 warning messages printed.



通过 DB2 解释工具可以检查 DB2 是如何执行这种递归查询的。嵌套的循环连接(NLJOIN)以一个临时结果表(TEMP)为基础,而这次连接的的结果又再次通过 UNION 被放到这个临时表中。


图 2. 对递归 SQL 的解释






回页首




Oracle 与 DB2 UDB 之间的差异

Oracle 通过使用 CONNECT BY PRIOR 提供了类似的特性。在 Oracle 中,上面的例子可以这样来实现:

SELECT sum(empcount) FROM STRUCREL
   CONNECT BY PRIOR superdept = deptid
     START WITH deptname = 'Production';



除了语法上的不同之外,DB2 与 Oracle 在功能性上也有差异。当使用 CONNECT BY PRIOR 时,Oracle 提供了内建的伪列 level。在 Oracle 中,下面的查询提供了所有的部门以及这些部门所在的层次结构:

SELECT deptname, level FROM departments
  CONNECT BY PRIOR superdept = deptid
  START WITH deptname = 'Samples & Co.';
DEPTNAME             LEVEL
-------------------- -----------
Samples & Co.                  1
Production                     2
QA                             3
Manufacturing                  3
Prebuilding                    4
Finalbuilding                  4
Sales                          2
North                          3
East                           3
South                          3       
West                           3
IT                             2



这种伪列通常用于限制那些查询的递归深度。例如,为了检索“Sales”这个部门的直属子部门,在 Oracle 中可以使用下面的查询:

SELECT deptname FROM departments CONNECT BY PRIOR superdept = deptid
START WITH deptname = 'Sales' AND level=2;
DEPTNAME
--------------------
North
East
South
West



在 DB2 中可以轻易地仿效这一特性,只需像下面这样在 CTE 中维护一个自定义的伪列:

WITH temptab(deptid, deptname, superdept, level) AS
   (    SELECT root.deptid, root.deptname, root.superdept, 1
            FROM departments root
            WHERE deptname='Sales'
     UNION ALL
        SELECT sub.deptid, sub.deptname, sub.superdept, super.level+1
            FROM departments sub, temptab super
            WHERE sub.superdept = super.deptid
   )
SELECT deptname FROM temptab WHERE level=2;



除了 level 伪列,在 DB2 和 Oracle 中另一个非常重要的差异就是由递归查询生成的结果集的搜索次序。在 Oracle 中,层次结构是由深度优先算法创建的。这样一来,当检索整个例子层次结构时,产生的结果集就是这个样子:

SELECT deptname, level FROM departments CONNECT BY PRIOR superdept = deptid
START WITH deptname = 'Samples & Co.;
DEPTNAME             LEVEL
-------------------- -----------
Samples & Co.                  1
Production                     2
QA                             3
Manufacturing                  3
Prebuilding                    4
Finalbuilding                  4
Sales                          2
North                          3
East                           3
South                          3       
West                           3
IT                             2



这个结果集说明,在查询延伸到邻节点之前,先要浏览完每个子节点。然而,在 DB2 中,层次结构是通过广度优先算法创建的:

WITH temptab(deptid, deptname, superdept, level) AS
   (    SELECT root.deptid, root.deptname, root.superdept, 1
            FROM departments root WHERE deptname='Samples & Co.'
     UNION ALL
        SELECT sub.deptid, sub.deptname, sub.superdept, super.level+1
            FROM departments sub, temptab super
            WHERE sub.superdept = super.deptid
   )
SELECT deptname, level FROM temptab;
DEPTNAME             LEVEL
-------------------- -----------
Samples & Co.                  1
Production                     2
Sales                          2
IT                             2
QA                             3
North                          3
East                           3
South                          3       
West                           3
Manufacturing                  3
Prebuilding                    4
Finalbuilding                  4



这意味着,结果集是一级一级地创建的。在本例中,这种差异或许算不了什么。但是在有些递归 SQL 的案例中,默认的排序次序则是至关重要的。例如,有一个包含讨论论坛的表:

CREATE TABLE discussion (postid INTEGER,
superid INTEGER,
title VARCHAR2(100),
text VARCHAR2(1000) )



为了获得对所有讨论线索的了解,在 Oracle 中可以这样来查询这个表:

SELECT RPAD('', level-1, '--') || title FROM discussion
CONNECT BY PRIOR superid = postid START WITH postid = 0;
1
-------------------------------------
Install Problem
--Re: Install Problem
----Re: Install Problem
------Re: Install Problem
--------Got it
--General comment
----Re: General Comment
Cannot find file
--Re: Cannot find file
----Re: Cannot find file
--Re: Cannot find file
Help! Documentation missing!



在 DB2 中使用无格式(plain)递归 SQL 时,不能以这样的次序重新得到结果集。如果非要尝试这么做的话,将得到下面的结果:

WITH temptab(superid, postid, title, text, level)
     AS
     (    SELECT root.superid, root.postid, root.title, root.text, 1
              FROM discussion root
              WHERE postid=0
       UNION ALL
          SELECT sub.superid, sub.postid, sub.title, sub.text, super.level+1
             FROM discussion sub, temptab super
             WHERE sub.superid = super.postid
     )
SELECT VARCHAR(REPEAT('--', level-1) || title , 60) FROM temptab;
1
------------------------------------
Problem Discussions
--Install Problem
--Cannot find file
--Help! Documentation missing!
----Re: Install Problem
----General comment
----Re: Cannot find file
----Re: Cannot find file
------Re: Install Problem
------Re: General Comment
------Re: Cannot find file
--------Re: Install Problem
----------Got it



显然,对于用户来说该结果集完全没有用,因为这里失去了论坛上各个帖子之间的相关性。






回页首




DB2 UDB 中仿效 Oracle 的行为

在 DB2 中,要生成 Oracle 中那样的深度优先次序,解决方案的基础就是引入一个附加的伪列,这个伪列可以在 ORDER BY 属性中使用。这个列的类型是 VARCHAR,包含了到每个节点的路径,其格式为“1.3.1”。另外还引入了一个用户定义的表函数,这个函数可以返回一个给定节点的所有子节点。通过将子节点的序号连接到上级节点的路径上,能够可靠地维护伪列代码。可以使用 DB2 的 RANK() 函数来检索一个子节点的序号。之后,递归查询从这个函数中进行选择,并提供当前节点的 id 以及它的路径作为输入。

下面的例子将创建与上一例子中 Oracle 中的查询完全一致的结果集:

CREATE FUNCTION GetResponses(code VARCHAR(100), superid INTEGER)
   RETURNS TABLE(code VARCHAR(100), superid INTEGER, postid INTEGER,
                 title VARCHAR(100), text VARCHAR(1000))
   READS SQL DATA DETERMINISTIC NO EXTERNAL ACTION
   RETURN SELECT GetResponses.code || '.'
                  || RTRIM(CHAR(RANK() OVER (ORDER BY postid))),
                 T.superid , T.postid, T.title, T.text
              FROM discussion T
              WHERE T.superid = GetResponses.superid;
WITH TEMPTAB(code, superid, postid, title, text, level)
     AS
     (    VALUES(CAST('1' AS VARCHAR(100)), CAST(NULL AS INTEGER), 0,  
                 CAST(NULL AS VARCHAR(100)), CAST(NULL AS VARCHAR(1000)), 0)
       UNION ALL
          SELECT t.code, t.superid, t.postid, t.title, t.text, level+1
             FROM TEMPTAB,
               TABLE(GetResponses(TEMPTAB.code, TEMPTAB.postid)) AS T
     )
SELECT VARCHAR(REPEAT('--', level-1) || title , 60)
     FROM TEMPTAB T
     WHERE t.superid is not null
     ORDER BY code;
1
-------------------------------------
Install Problem
--Re: Install Problem
----Re: Install Problem
------Re: Install Problem
--------Got it
--General comment
----Re: General Comment
Cannot find file
--Re: Cannot find file
----Re: Cannot find file
--Re: Cannot find file
Help! Documentation missing!



为了使应用程序中的语句简单一些,这里同样可以将这些递归语句包装到一个 UDF 中。






回页首




一种更好地使用 DB2 Node 类型的方法

您必须清楚,基于一个字符串使用伪列以强制性地使结果集具有某一特定的次序,这只能保证总体上的层次结构次序。如果某个节点的直属子节点的数量超过 9 的话,这样做未必能够正确地对这些子节点排序。这是因为像“1.2.13”这样的字符串比“1.2.13”有着更低的次序。但是从语义上讲,事情刚好相反。如果您要依赖于这种方法,而又不能保证最多只有 9 个直属子节点,那么您就决不能为伪列使用一个字符串。

相反,您可以使用 DB2 Node 类型,这是一个 DB2 扩展,当前在 IBM DB2 Developer Domain 上(由 Jacques Roy 撰写的 Using the Node Data Type to Solve Problems with Hierarchies in DB2 Universal Database )可以获得。您必须使用最低版本为 1.1 的 Node 类型扩展。可以通过 nodeVersion() 函数来检查版本。如果该函数不存在,那么就说明您使用的是更老版本的 DB2 Node 类型。

因此,现在我们不使用 VARCHAR 类型来维护伪列代码,而是使用用户定义类型的 Node。下面的例子对此作了演示。该例子将创建与上面使用 VARCHAR 的例子一样的结果集:

CREATE FUNCTION GetResponsesN(code Node, superid INTEGER)
   RETURNS TABLE(code Node, superid INTEGER, postid INTEGER,
                 title VARCHAR(100), text VARCHAR(1000))
   READS SQL DATA DETERMINISTIC NO EXTERNAL ACTION
RETURN SELECT nodeInput(nodeOutput(GetResponsesN.code) || '.' ||  
                                 RTRIM(CHAR(RANK() OVER (ORDER BY postid)))),
              T.superid , T.postid, T.title, T.text
         FROM discussion T
         WHERE T.superid = GetResponsesN.superid;
WITH TEMPTAB(code, superid, postid, title, text, level)
     AS
     (    VALUES(nodeInput('1.1'), CAST(NULL AS INTEGER), 0,  
                 CAST(NULL AS VARCHAR(100)), CAST(NULL AS VARCHAR(1000)), 0)
       UNION ALL
          SELECT t.code, t.superid, t.postid, t.title, t.text, level+1
             FROM TEMPTAB,
               TABLE(GetResponsesN(TEMPTAB.code, TEMPTAB.postid)) AS T
     )
SELECT VARCHAR(REPEAT('--', level-1) || title , 60)
     FROM TEMPTAB T
     WHERE t.superid is not null
     ORDER BY code;
1
-------------------------------------
Install Problem
--Re: Install Problem
----Re: Install Problem
------Re: Install Problem
--------Got it
--General comment
----Re: General Comment
Cannot find file
--Re: Cannot find file
----Re: Cannot find file
--Re: Cannot find file
Help! Documentation missing!



为了创建一个 Node 值,我们必须使用函数 nodeInput(),并为之提供像“1.2” 这样的一个字符串作为输入。对于根节点,输入是“1.1”(由于 DB2 节点类型的具体实现,我们只能从 1.1 开始,而不是从 1 开始)。对于所有其他的节点,我们同样使用 DB2 的 RANK() 函数来为直属子节点分配序号。这是在 GetResponsesN() 函数中进行的。之后,这个序号被连接到上级节点的字符表示(通过 nodeOutput() 获得)上,再将这样得到的字符串作为输入,通过 nodeInput() 函数创建新的 Node 值。






回页首




结束语

DB2 UDB 为递归 SQL 而设的方法提供了一种非常灵活的方式来处理层次结构。正如本文所演示的,DB2 UDB 能够轻易地仿效其他数据库供应商的行为,因为 DB2 通过用户定义的函数提供了方便自如的可扩展性。而且,DB2 UDB 还提供了处理非常高级的递归查询的方法,例如那些在单个表中有多重层次结构的情况下的递归查询。通过使用我描述过的这些技术,在移植应用程序时您可以充分利用 DB2 的长处。






回页首




免责声明

本文包含示例代码。IBM 授予您(“被许可方”)使用这个样本代码的非专有的、版权免费的许可证。然而,该样本代码是以“按现状”的基础提供的,没有任何形式的(不论是明示的,还是默示的)保证,包括对适销性、适用于特定用途或非侵权性的默示保证。IBM 及其许可方不对被许可方由于使用该软件所导致的任何损失负责。任何情况下,无论损失是如何发生的,也不管责任条款怎样,IBM 或其许可方都不对由使用该软件或不能使用该软件所引起的收入的减少、利润的损失或数据的丢失,或者直接的、间接的、特殊的、由此产生的、附带的损失或惩罚性的损失赔偿负责,即使 IBM 已经被明确告知此类损害的可能性,也是如此。






回页首




致谢

感谢 Serge Rielau (IBM)为我指引了正确的方向;感谢 Jacques Roy (IBM)认真审查并正确无误地输入了这篇文档;同时也感谢 Jacques Terrasse (Aldata)为我提供了机会,使我得以在客户环境中得出这一解决方案。






回页首




下载示例

描述 文件类型 文件大小 下载方法
recursive.zip zip 2 KB HTTP   



关于作者



  Torsten Steinbach是 IBM Partner Enablement in EMEA 在 DB2 和 WebSphere ® 方面的高级技术顾问。可以通过 torsten@de.ibm.com与他联系。

使用道具 举报

回复
论坛徽章:
0
10#
发表于 2009-11-21 16:30 | 只看该作者
mark

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表