码迷,mamicode.com
首页 > 其他好文 > 详细

实现级联查询

时间:2015-03-17 12:42:05      阅读:2031      评论:0      收藏:0      [点我收藏+]

标签:

关于数据库设计中的分级分层问题的总结(适用于组织结构图及家谱等问题)
大家在很多地方都会遇到诸如此类的问题,如:
在一个组织中,从高层领导到中层领导到普通职工,有着一种分级的关系,这些关系在关系型数据库中表示时,有它特别的方法。
有些人把它们分别建立在不同的表中,建立相应的关系,这虽然是一种解决办法,但如果在应用中,它会给软件带来诸多不便,特别是程序健壮性方法,如果此时需要多加一个职位,这种解决办法就需要多加一张表,那此时对应用程序的改动将可想而知。
我在此处提供的方法并不是什么独特的方法,而是大家所属知的方法,但在我们的论坛上经常有人问到并且我再三进行解答。
因此我一方面将这些帖的问题收集一下,再加上全面的解释,组织成这样一篇文章,希望对大家有所帮助。
如有不正确之处请大家提出意见。
问题一:
想设计一个组织结构管理的数据库模型,能让用户自由定义组织结构层次和关系,大家有没有好的意见和经典的结构模型?
解决:
其实一个层次的模型在关系数据库本身就是个问题。根本问题在于关系模型的第一范式就要求属性分为不可再分,这样的话就直接造成层次结构实现的复杂性。如果 一个属性可以包含多个内容的话,那就可以有这样一个属性,叫做"下级"的属性,它包含了此成员的所有下级,但这样是不可取的。
虽然这样,但组织结构这个问题比较容易解决。原因是组织结构本身的结构比较简单。
在设计的时候只要做一个指向本身主键的属性,也就是说这个属性的外键就是本表的主键,在查询的时候很简单,只要做一个自连接就可以了,也就是
select b.* from (t1) as a,(t1) as b where a.p1=b.p2 and a.p1="bill"
t1为表,p1为主键,p2为下属,它指向它的上级p1。上面这条语句查询的就是bill的下属名单。
select a.* from (t1) as a,(t1) as b where a.p1=b.p2 and b.p2="bill"
这条语句查询的就是bill的上级,这就实现了多层。
问题二:
如何在数据库中表示这样的数据,希望在论坛中,做出把一些人加入黑名单的功能,描述:
这个数据库保存每个用户所讨厌的用户,例如:
mary tom john
sam joe
以上表示mary讨厌tom和john, sam讨厌joe
解决:
这在关系代数里是一个自连接多对多关系数据库,ER图不便画出,在这里我只说明解决方案。
当然有一个注册用户表,如:
CREATE TABLE [TB_user] (
[userid] [varchar] (20) COLLATE Chinese_PRC_CI_AS NOT NULL ,
[username] [varchar] (10) COLLATE Chinese_PRC_CI_AS NULL ,
CONSTRAINT [PK_TB_user] PRIMARY KEY CLUSTERED
(
[userid]
) ON [PRIMARY]
) ON [PRIMARY]
GO
再来就是保存讨厌对象的表:
CREATE TABLE [bedfriend] (
[bedid] [varchar] (20) COLLATE Chinese_PRC_CI_AS NOT NULL ,
[userid1] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,
[userid2] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,
CONSTRAINT [PK_bedfriend] PRIMARY KEY CLUSTERED
(
[bedid]
) ON [PRIMARY]
) ON [PRIMARY]
GO
OK了,TB_user里存的是注册用户
bedfrend表里存的是如下信息:
bedfrend
---------------------
bedid   userid1   userid2
1         1          2
2         1          5
3         1          8
4         2          3
5         2          4
6         3          6
注意有一点,就是,这个关系设计的是如果bill讨厌kate,那kate不一定讨厌bill
原因大家都知道,kate喜欢bill嘛,bill喜欢nana

问题三:
用户表:uid[用户ID],depid[所属部门ID]
部门表:depid[部门ID],pdepid[上级部门ID]
函数:UidInDepSub(depid int,uid int) --判断uid是否属于depid[部门ID]或其下级部门ID。
结果:select * from news where dbo.UidInDepSub(depid,uid)>1 --可以用于select中判断,此处需要多层递归判断,层次不定。
请问此函数应该怎么写?
解决:
CREATE TABLE [depart] (
 [depid] [int] NOT NULL ,
 [pdepid] [int] NULL ,
 CONSTRAINT [PK_depart] PRIMARY KEY CLUSTERED
 (
 [depid]
 ) ON [PRIMARY] ,
 CONSTRAINT [FK_depart_depart] FOREIGN KEY
 (
 [pdepid]
 ) REFERENCES [depart] (
 [depid]
 )
) ON [PRIMARY]
--向depart加入数据
insert into depart values(1,null)--顶层的上级一定要为null
insert into depart values(2,1)
insert into depart values(3,1)
insert into depart values(4,2)
insert into depart values(5,2)
insert into depart values(6,3)
insert into depart values(7,4)
insert into depart values(8,4)
insert into depart values(9,5)
insert into depart values(10,6)

create function billfun(@departid int, @uid int)
returns int
as
begin
 declare @temp int
 SELECT @temp=pdepid
 FROM dbo.depart
 WHERE (depid = @uid)
 if(@temp=@departid)
 RETURN 1
 if(@temp is null)
 RETURN 0
 RETURN (dbo.billfun(@departid,@temp))
end

select dbo.billfun(2,8)
--为1
select dbo.billfun(3,8)
--为0
上面的函数是一个递归函数,楼主当时看我的程序短还以为不能用呢。其实它就是一直递归查询uid是不是属于它们的高n级部门,而非高一级部门。
 
给你一个示例:
/*
1。实现级联查询 例如,提供一个员工编号,能够查询出该
员工的上级和间接上级的信息。
2。实现级联查询 例如,提供一个员工编号,能够查询出该
员工的下级和间接下级的信息。
3。实现级联删除。例如,指定一个员工编号,当删除该员工
的基本信息的时候,该员工的所有下级全部被删除。无论
上级还是下级,都包括员工自己。
4。实现级联更新。当一个员工的工号改变时,所有以该员工
为直接上级的员工的reportto字段的值都变为新的工号。
*/

create table emp(
 
  eid int primary key,
   ename varchar(20),
   sal money,
   reportto int references emp(eid)  --该员工的直接上级
   )
--drop table emp
   insert into emp select 1001,‘rain‘,1000,null
   union select 1002,‘ann‘,3000,1001
   union select 1003,‘lopez‘,2000,1001
   union select 1004,‘nakata‘,3000,1002
   union select 1005,‘tae‘,1500,1004
   union select 1006,‘raul‘,900,1004
   union select 1007,‘owen‘,15000,1006
--select * from emp
/*实现级联查询 例如,提供一个员工编号,能够查询出该员工的上级和间接上级的信息。*/
create proc p1 @eid int
as
begin
declare @re table(eid int,level int)
declare @l int
set @l=0
insert @re select @eid,@l
while @@rowcount>0
--全局变量@@rowcount,记录上次操作影响的行数
begin
set @l=@l+1
insert @re select a.reportto,@l from emp as a,@re as b where a.eid=b.eid and b.level=@l-1
--循环将当前记录的直接上级插入@re
end
select a.* from @re as b inner join emp as a on a.eid = b.eid
end
--drop proc p1
exec p1 1005

/*实现级联查询 例如,提供一个员工编号,能够查询出该员工的下级和间接下级的信息。*/
create proc p2 @eid int
   as
   begin
    declare @re table(eid int,level int)
    declare @l int
    set @l=0
    insert @re select @eid,@l
    while @@rowcount>0
    --全局变量@@rowcount,记录上次操作影响的行数
    begin
    set @l=@l+1
    insert @re select a.eid,@l from emp as a,@re as b where a.reportto=b.eid and  b.level=@l-1
    --循环将当前记录的直接下级插入@re
    end
    select a.* from @re as b inner join emp as a on a.eid = b.eid
    end
    --drop proc p2
    exec p2 1004

实现级联查询

标签:

原文地址:http://my.oschina.net/lonelyzero/blog/387843

(1)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!