查看: 12901|回复: 23

[精华] 关于db2递归的一个例子

[复制链接]
招聘 : Linux运维
论坛徽章:
235
紫蜘蛛
日期:2007-09-26 17:05:46玉兔
日期:2007-09-26 17:05:05现任管理团队成员
日期:2011-05-07 01:45:08玉兔
日期:2006-08-29 20:38:48紫蜘蛛
日期:2007-09-26 17:05:34阿斯顿马丁
日期:2013-11-19 10:38:16奔驰
日期:2013-10-16 09:08:58红旗
日期:2014-01-09 11:57:39路虎
日期:2013-08-13 14:52:35林肯
日期:2015-05-19 13:01:16
跳转到指定楼层
1#
发表于 2008-3-14 13:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
关于db2递归的一个例子

ORACLE中大家可能对递归(在oracle中很多人称作家族树)已经很熟悉,因为ORACLE中的递归比较简单,也容易掌握!刚接触DB2的时候,也碰到过递归的问题,因为研究的人少所以刚接触的时候,对db2有一种神秘的感觉,db2是很强大的这个误用质疑!就sql而言,凡是oracle用sql能实现的,db2肯定或多或少也可以实现,只是逻辑复杂点!因为一直有人提问这边的问题,所以今天抽时间写了一点

首先,关于db2的递归的一点基础知识:

db2中的递归查询使用with来实现,也称为公共表达式,公共表达式在select语句的开始部分采用with子句的形式,在使用公共表达式的查询中可以多次使用它,并且公共表达式还可以通过取别名来连接到他本身,这样的话就可以达到循环的目的。

递归查询通常有3个部分需要定义:

一:一个公共表达式形式的虚拟表。
二:一个初始化表。
三:一个与虚拟表进行完全内连接的辅助表。

需要使用UNION all合并上边3个查询,然后用select从递归输出中得到最终的结果。

大体上如下形式
with XX(x1,x2,x3) as  -------@0
(
select a.s,a.s1 from a  ----@1
union all  ----@2
select * from a,xx where a.s=xx.x1 ------@3
)
select ... from xx where .... -------@4

@0:为with体,即虚拟表
@1:为初始化表,这里需要定义初始化的一些行,也就是你递归的出发点,或者说父行,这部分逻辑只执行一次,它的结果作为虚拟表递归的初始化内容。
@2:这里必须用UNION all
@3:这里需要定义递归的条件(辅助表),这里定义递归的逻辑,需要注意的是父行和子行进行连接的时候逻辑一定要清楚父子关系,不然很容易变成死循环的,这里首先将初始化表的结果作为条件进行查询,在把执行的结果添加到虚拟表中,只要这里能查询出来记录,那么就会进行下一步递归循环。
@4:这里就是对虚拟表的查询语句。

关于关于递归更多的知识,可以到google中找更专业的资料,这里只讲我理解的一些东西。
这里举一个简单点的例子:

-------------------------------
Q:求一SQL--关于数据纵横转换的


假设表A中有以下数据
A1 1 01
A1 1 02
A1 1 03
A2 1 01
A2 2 01
A2 2 02
现希望一SQL能够查出下边这样的数据(暂时假设表A中第三列只有01-03这三种可能值)
A1 1 01-02-03
A2 1 01
A2 2 01-02

A:这个查询牵扯到全表的递归循环,用一般的sql语句很难搞定!

这是我刚写的一个递归语句!分享给大家看看!

因为懒,所以把建立测试表的逻辑也略去了,这里用with来生成上边的测试数据。
with t (t1,t2,t3) as (
values
('A1', 1 ,'01'),('A1' ,1, '02'),('A1', 1, '03'),
('A2', 1, '01'),('A2', 2, '01'),('A2', 2 ,'02')
)
select * from t;
测试一下:

C:\>db2 connect to dw

   数据库连接信息

数据库服务器         = DB2/NT 8.2.0
SQL 授权标识         = DB2ADMIN
本地数据库别名       = DW


C:\>db2 with t (t1,t2,t3) as ( \
db2 (cont.) => values \
db2 (cont.) => ('A1', 1 ,'01'),('A1' ,1, '02'),('A1', 1, '03'), \
db2 (cont.) => ('A2', 1, '01'),('A2', 2, '01'),('A2', 2 ,'02') \
db2 (cont.) => ) \
db2 (cont.) => select * from t

T1 T2          T3
-- ----------- --
A1           1 01
A1           1 02
A1           1 03
A2           1 01
A2           2 01
A2           2 02

  6 条记录已选择。


现在公布递归逻辑,sql如下:
with t (t1,t2,t3) as (
values
('A1', 1 ,'01'),('A1' ,1, '02'),('A1', 1, '03'),
('A2', 1, '01'),('A2', 2, '01'),('A2', 2 ,'02')
),
t1(t11,t22,t33,t44,t55) as (
select t1,t2,t3,rownumber() over(partition by t1,t2),rownumber() over(partition by t1,t2) as t4 from t
),
t3(s1,s2,s3,s4,s5) as(
select t11,t22,cast(t33 as varchar(100)),t44,t55 from t1 where T44 =1 and t55=1
union all
select a.s1,a.s2,cast(a.s3||'-'||b.t33 as varchar(100)),a.s4+1,a.s5 from t3 a,t1 b
where a.s1=b.t11 and a.s4 = b.t55-1)
select s1,s2,s3 from t3 x where x.s4=(select max(s4) from t3 y where x.s1=y.s1) order by s1,s2
;

测试一下:


C:\>db2 connect to dw

   数据库连接信息

数据库服务器         = DB2/NT 8.2.0
SQL 授权标识         = DB2ADMIN
本地数据库别名       = DW


C:\>db2 with t (t1,t2,t3) as ( \
db2 (cont.) =>  values \
db2 (cont.) => ('A1', 1 ,'01'),('A1' ,1, '02'),('A1', 1, '03'), \
db2 (cont.) => ('A2', 1, '01'),('A2', 2, '01'),('A2', 2 ,'02') \
db2 (cont.) => ), \
db2 (cont.) =>  t1(t11,t22,t33,t44,t55) as ( \
db2 (cont.) =>  select t1,t2,t3,rownumber() over(partition by t1,t2),rownumber() over(partition by t1,t2) as t4 from t \
db2 (cont.) => ), \
db2 (cont.) => t3(s1,s2,s3,s4,s5) as( \
db2 (cont.) => select t11,t22,cast(t33 as varchar(100)),t44,t55 from t1 where T44 =1 and t55=1 \
db2 (cont.) =>  union all \
db2 (cont.) => select a.s1,a.s2,cast(a.s3||'-'||b.t33 as varchar(100)),a.s4+1,a.s5 from t3 a,t1 b \
db2 (cont.) =>  where a.s1=b.t11 and a.s4 = b.t55-1) \
db2 (cont.) => select s1,s2,s3 from t3 x where x.s4=(select max(s4) from t3 y where x.s1=y.s1) order by s1,s2

S1 S2          S3
-- ----------- ----------------------------------------------------------------------------------------------------
SQL0347W  递归公共表表达式 "DB2ADMIN.T3" 可能包含无限循环。  SQLSTATE=01605

A1           1 01-02-03
A2           1 01-02
A2           2 01-02

  已选择 3 条记录,打印 1 条警告消息。


ok,搞定!

个人认为db2还是很强大的!大家一起学习!
论坛徽章:
3
生肖徽章2007版:牛
日期:2009-02-17 13:26:03祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:19:09
2#
发表于 2008-3-14 14:12 | 只看该作者
厉害!

使用道具 举报

回复
招聘 : Linux运维
论坛徽章:
235
紫蜘蛛
日期:2007-09-26 17:05:46玉兔
日期:2007-09-26 17:05:05现任管理团队成员
日期:2011-05-07 01:45:08玉兔
日期:2006-08-29 20:38:48紫蜘蛛
日期:2007-09-26 17:05:34阿斯顿马丁
日期:2013-11-19 10:38:16奔驰
日期:2013-10-16 09:08:58红旗
日期:2014-01-09 11:57:39路虎
日期:2013-08-13 14:52:35林肯
日期:2015-05-19 13:01:16
3#
 楼主| 发表于 2008-3-14 14:18 | 只看该作者

上边的代码在逻辑可以了,还需要改改

with t (t1,t2,t3) as (
values
('A1', 1 ,'01'),('A1' ,1, '02'),('A1', 1, '03'),
('A2', 1, '01'),('A2', 2, '01'),('A2', 2 ,'02')
),
t1(t11,t22,t33,t44,t55) as (
select t1,t2,t3,rownumber() over(partition by t1,t2),rownumber() over(partition by t1,t2) as t4 from t
),
t3(s1,s2,s3,s4,s5) as(
select t11,t22,cast(t33 as varchar(100)),t44,t55 from t1 where T44 =1 and t55=1
union all
select a.s1,a.s2,cast(a.s3||'-'||b.t33 as varchar(100)),a.s4+1,a.s5 from t3 a,t1 b where a.s1=b.t11 and a.s2=b.t22  and a.s4 = b.t55-1
)
select s1,s2,s3 from t3 x where x.s4=(select max(s4) from t3 y where x.s1=y.s1 and x.s2=y.s2) order by s1,s2
;
------------------------------------
'A1'        1        '01-02-03'
'A2'        1        01
'A2'        2        '01-02'

这样才对!

使用道具 举报

回复
论坛徽章:
3
生肖徽章2007版:牛
日期:2009-02-17 13:26:03祖国60周年纪念徽章
日期:2009-10-09 08:28:002010新春纪念徽章
日期:2010-03-01 11:19:09
4#
发表于 2008-3-14 14:36 | 只看该作者
能给解释一下rownumber() over(partition by t1,t2),
是什么意思吗?

使用道具 举报

回复
招聘 : Linux运维
论坛徽章:
235
紫蜘蛛
日期:2007-09-26 17:05:46玉兔
日期:2007-09-26 17:05:05现任管理团队成员
日期:2011-05-07 01:45:08玉兔
日期:2006-08-29 20:38:48紫蜘蛛
日期:2007-09-26 17:05:34阿斯顿马丁
日期:2013-11-19 10:38:16奔驰
日期:2013-10-16 09:08:58红旗
日期:2014-01-09 11:57:39路虎
日期:2013-08-13 14:52:35林肯
日期:2015-05-19 13:01:16
5#
 楼主| 发表于 2008-3-14 15:06 | 只看该作者

使用道具 举报

回复
论坛徽章:
0
6#
发表于 2008-3-14 15:22 | 只看该作者
终于搞懂你的逻辑了,终于明白这个问题的递归所在了,再次感谢。

使用道具 举报

回复
论坛徽章:
42
ITPUB元老
日期:2005-09-09 13:45:35马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:14优秀写手
日期:2013-12-18 09:29:09ITPUB 11周年纪念徽章
日期:2012-10-09 18:03:32版主3段
日期:2012-05-15 15:24:112012新春纪念徽章
日期:2012-02-13 15:13:362012新春纪念徽章
日期:2012-02-13 15:13:36
7#
发表于 2008-3-14 16:29 | 只看该作者
不错不错,我来授精

使用道具 举报

回复
招聘 : Linux运维
论坛徽章:
235
紫蜘蛛
日期:2007-09-26 17:05:46玉兔
日期:2007-09-26 17:05:05现任管理团队成员
日期:2011-05-07 01:45:08玉兔
日期:2006-08-29 20:38:48紫蜘蛛
日期:2007-09-26 17:05:34阿斯顿马丁
日期:2013-11-19 10:38:16奔驰
日期:2013-10-16 09:08:58红旗
日期:2014-01-09 11:57:39路虎
日期:2013-08-13 14:52:35林肯
日期:2015-05-19 13:01:16
8#
 楼主| 发表于 2008-3-14 16:31 | 只看该作者

使用道具 举报

回复
招聘 : c/c++研发
论坛徽章:
45
技术图书徽章
日期:2014-03-10 14:09:192012新春纪念徽章
日期:2012-02-13 15:12:092012新春纪念徽章
日期:2012-02-13 15:12:092012新春纪念徽章
日期:2012-01-04 11:51:22ITPUB十周年纪念徽章
日期:2011-11-01 16:21:15现任管理团队成员
日期:2011-05-07 01:45:082011新春纪念徽章
日期:2011-01-25 15:42:562011新春纪念徽章
日期:2011-01-25 15:42:332011新春纪念徽章
日期:2011-01-25 15:42:152011新春纪念徽章
日期:2011-01-25 15:41:50
9#
发表于 2008-3-14 19:43 | 只看该作者
强悍,这年头非常懂db2语法的感觉比DB2 DBA还少

使用道具 举报

回复
招聘 : Linux运维
论坛徽章:
235
紫蜘蛛
日期:2007-09-26 17:05:46玉兔
日期:2007-09-26 17:05:05现任管理团队成员
日期:2011-05-07 01:45:08玉兔
日期:2006-08-29 20:38:48紫蜘蛛
日期:2007-09-26 17:05:34阿斯顿马丁
日期:2013-11-19 10:38:16奔驰
日期:2013-10-16 09:08:58红旗
日期:2014-01-09 11:57:39路虎
日期:2013-08-13 14:52:35林肯
日期:2015-05-19 13:01:16
10#
 楼主| 发表于 2008-3-14 19:50 | 只看该作者
过赞了,受宠若惊!

要不,狼拉我入IBM吧!!!



原帖由 wangzhonnew 于 2008-3-14 19:43 发表
强悍,这年头非常懂db2语法的感觉比DB2 DBA还少

使用道具 举报

回复

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

本版积分规则 发表回复

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