大家在很多地方都会遇到诸如此类的问题,如:
在一个组织中,从高层领导到中层领导到普通职工,有着一种分级的关系,这些关系在关系型数据库中表示时,有它特别的方法。
有些人把它们分别建立在不同的表中,建立相应的关系,这虽然是一种解决办法,但如果在应用中,它会给软件带来诸多不便,特别是程序健壮性方法,如果此时需要多加一个职位,这种解决办法就需要多加一张表,那此时对应用程序的改动将可想而知。
我在此处提供的方法并不是什么独特的方法,而是大家所属知的方法,但在我们的论坛上经常有人问到并且我再三进行解答。
因此我一方面将这些帖的问题收集一下,再加上全面的解释,组织成这样一篇文章,希望对大家有所帮助。
如有不正确之处请大家提出意见。
问题一:
想设计一个组织结构管理的数据库模型,能让用户自由定义组织结构层次和关系,大家有没有好的意见和经典的结构模型?
解决:
其 实一个层次的模型在关系数据库本身就是个问题。根本问题在于关系模型的第一范式就要求属性分为不可再分,这样的话就直接造成层次结构实现的复杂性。如果一 个属性可以包含多个内容的话,那就可以有这样一个属性,叫做"下级"的属性,它包含了此成员的所有下级,但这样是不可取的。
虽然这样,但组织结构这个问题比较容易解决。原因是组织结构本身的结构比较简单。
在设计的时候只要做一个指向本身主键的属性,也就是说这个属性的外键就是本表的主键,在查询的时候很简单,只要做一个自连接就可以了,也就是
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级部门,而非高一级部门。