首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 培训 数据库 书店 程序员
中国软件网
欢迎您:游客 | 登录 注册 帮助
  • 关于树形数据在数据库中存储的讨论 [已结贴,结贴人:HEROWANG]
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • HEROWANG
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    • 揭帖率:
    发表于:2008-08-22 10:34:42 楼主
        关于树形数据在数据库中存储的讨论,希望大家发表意见,我先抛砖引玉
        对于树形数据在数据库中的存储一般有两种方法,一种使用单节点,一种使用双节点。单节点:表的结构为id,name
    双节点为id,pid,name。但是两种方法各有各的优缺点。第一种,在数据的查询方面,尤其在查找子节点与父节点时非常的简单方便,但是这种方法对节点的编码要求比较高,另外对每一层的节点数会有一定的限制。而第二种在数据的修改比较方便,但是在查找子节点与父节点不是很方便。两者的优缺点就不详细说了。
        对树形数据的存储,想了一种新的方法,其实也不算新的方法,就是在上面两种方法上略加改动,把上面的两种方法结合起来存储树形数据。
        思路:建立两张表,一张保存节点的信息,表结构为id,pid,name。另一张表结构为:id,level,path,其中level为每个节点的层数(数据结构中成为深度),path保存从它的路径。然后在第一张表上建立insert,update,delete触发器,当对节点进行操作时,在第一张表上进行操作,然后通过触发器来更新第二张表。代码写了一部分,因为本人比较懒,触发器的插入和删除写了一些,更新的触发器没有写,有心人可以自己写一下,然后贴出来,大家一起共享一下。
      代码如下,当然其中还是有一些问题,问题在代码中会标出来,同时希望大家能把自己的想法写出来,我再进行修改。另外,代码中参照了邹老师的一些代码
    /*******************************************************
      作者:trieagle
      版本:v1.0
      名称:树形数据在数据中的存储
      备注:
    ********************************************************/
      create table t_tree
    (id varchar(10) primary key,
    pid varchar(10),
    name varchar(50)
    )

    create table t_treepath
    (id varchar(10) primary key,
    level int,
    path varchar(500)
    )
    --在t_tree中插入节点的时候同时在t_treepath中记录该节点的路径

    if object_id('t_insertpath') <>0 drop trigger t_insertpath
    go
    create trigger t_insertpath
    on t_tree
    for insert
    as
    begin
      declare @level int
      set @level=1
      if exists(select pid from inserted where pid is null or pid='')
        insert into t_treepath
        select id ,@level,id
        from inserted
      else if exists(select * from inserted,t_treepath where inserted.pid=t_treepath.id)
        insert into t_treepath
        select inserted.id,t_treepath.level+1,t_treepath.path+inserted.id
      from inserted,t_treepath
    where inserted.pid=t_treepath.id
      else
          begin
            print '插入节点的父节点不存在,插入取消'
    rollback
          end
    end
    --删除一个节点的时候,同时对节点的路径进行删除
    if object_id('t_deleteNode') <>0 drop trigger t_deleteNode
    go
    create trigger t_deleteNode
    on t_tree
    for delete
    as
    begin
    declare @count int,@startpos int,@len int
    select  @count=count(*) from deleted
    if @count=(select count(*) from t_treepath)
      delete from t_treepath
    else if @count=1
        begin
            select @startpos=len(path)-len(deleted.id),@len=len(deleted.id)
            from deleted,t_treepath
            where deleted.id=t_treepath.id
     
            update t_tree
            set pid=(select pid from deleted)
            where pid=(select id from deleted)
           
            update t_treepath
            set level=level-1,
                path=stuff(path,@startpos+1,@len,'')
            where id in (select id from t_treepath where path like (select path from                t_treepath,deleted where t_treepath.id=deleted.id)+'%')
     
          delete from t_treepath
          where id=(select id from deleted)
        end
        else--删除多行的时候,没有想到很好的判断条件,所以就姑且认为删除多行的时候就是删除某个节点及该节点的所有子节点
          delete from t_treepath where id in (select id from deleted)     
    end
    --更新节点的时候,同时更新该节点的路径
    if object_id('t_updatePath') <>0 drop trigger t_updatePath
    go
    create trigger t_updatePath
    on t_tree
    for update
    as
    begin
    --此人很懒,没有写
    end
    --按照广度搜素

    select t_tree.* from t_tree,t_treepath
    where t_tree.id=t_treepath.id
    order by level,t_treepath.id

    --按照深度搜素
    select t_tree.* from t_tree,t_treepath
    where t_tree.id=t_treepath.id
    order by path

    --显示树形数据

    select space(level*2)+'|--'+name
    from t_tree,t_treepath
    where t_tree.id=t_treepath.id
    order by path


    --查找子节点
    select t_tree.*
    from t_tree,t_treepath
    where t_tree.id=t_treepath.id
          and path like'%002%'


    select t_tree.*
    from t_tree,t_treepath
    where t_tree.id=t_treepath.id
          and path like '%'+(select id from t_tree where name='烟台市')+'%'

    --查找父节点

    select t_tree.*
    from t_tree,t_treepath
    where t_tree.id=t_treepath.id  and
          '001002' like path+'%'

    select t_tree.*
    from t_tree,t_treepath
    where t_tree.id=t_treepath.id
          and (select path from  t_tree,t_treepath where t_tree.id=t_treepath.id and name='烟台市') like path+'%'
    --删除的时候,要删除某个节点的所有子节点

    delete from t_tree
    where id in (
        select id
        from t_tree,t_treepath
        where t_tree.id=t_treepath.id
          and path like'%002%')
    200  修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • HEROWANG
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 10:34:591楼 得分:0
    sf
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • feng2112
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 10:55:122楼 得分:5
    mark
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ws_hgo
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:01:023楼 得分:5
    严重关注事态
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • zoujp_xyz
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:13:334楼 得分:5
    jf
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • mengmou
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:15:445楼 得分:5
    自从2005有了递归CTE后,树形数据处理起来就简单多了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • M1CR0S0FT
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:17:466楼 得分:5
    在2008里,树型问题已经可以很好的驾驭了.
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • hery2002
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    • 2

      2

    发表于:2008-08-22 11:18:067楼 得分:5
    H.H.H.Q.D.
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wufeng4552
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:19:128楼 得分:5
    帮你顶吧~~~~~
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ltmlen
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:19:499楼 得分:5
    好长,我漫漫看
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • gxg353
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:21:0910楼 得分:5
    JF
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • only_endure
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:25:2411楼 得分:5
    o.n.l.y.e.n.d.u.r.e
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • luyesql
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 11:31:5512楼 得分:5
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • Herb2
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 12:15:4613楼 得分:100
    楼主的总结非常好。
    但我考虑几个问题:
    一个问题是对业务的支持问题:
      这个问题主要表现在节点变更和删除节点方面:
        在节点变更时存在子节点的路径和层次同步变更的问题,你在例子中没有完成变更触发器,
            可见你想到了这个问题,但一时没有想好怎么完成,并且还可能存在同时变更多个节点的问题。
        在节点的删除中存在多行删除的问题,并不是你认为的就是子树的删除,可能是任意的节点多行删除,
            而这一块的复杂度不低于节点变更。
    第二个问题是应用问题:
      很多情况下仅仅只有层次和路径是不能解决问题,所以发非这么大的力气来解决问题,而得到的结果并不能
      很好的解决需求问题,应该说是得不偿失。

    我个人认为解决树的问题完全可以通过函数来完成(有人说2008已经很好的解决了,我不了解)
    函数分两类
    一类是单节点函数:也就是求节点的层次、路径、子节点数目、父节点、同级节点数、路径包含等,这种函数可以可以采用标准命名法以后直接使用:
    比如用组织结构树:ORI
    可以定义一组函数:ORI_LEVEL(ID INT),ORI_PATH(ID INT),ORI_ITEMCOUNT(ID INT),ORI_PARENTNAME(ID INT)...
    相信有这样一组函数,应该可以很好的解决单节点的问题。
    第二类是多节点函数:给出的参数是多节点的,返回的结果也可能是多节点的。
    这组函数较为复杂,常用的是求所有子节点、求父节点、返回子节点数目。
    这组函数主要要解决参数的输入输出问题,输出可以采用表函数,输入目前我能想到是字符串。
    比如求所有子节点:ORI_M_ITEM(IDC VARCHAR(800)) RETURNS TABLE(ID INT)
    相信第二组函数能够很好的解决节点批量操作问题。

    这样对数的处理就简单多了。
    现在,还没有想出不能处理的情况。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • guoli0813
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 12:17:2714楼 得分:5
    留下脚印,学习了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • Herb2
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 12:40:1415楼 得分:0
    举一个删除子树的例子:
    树:ORI(ID INT,PID INT,NAME VARCHAR(100))
    可以用两种方法:
    一种采用单节点函数:
    create function ORI_INCLUDEID(ID INT,IID INT)
    --判断指定节点(ID)的路径中是否包含需要查找的节点(IID),返回0(不包含)、1(包含)
    returns int
    as
    begin
    ...
    end
    go
    --删除子树
    delete ORI where ORI_INCLUDEID(id,3)=1
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • HEROWANG
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 14:03:4516楼 得分:0
    自己顶一下,不要沉下去了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • bing110
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 14:04:0417楼 得分:5
    先学习了。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • roger0705sally
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 14:51:3418楼 得分:5
    学习·········
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ChinaJiaBing
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 15:06:0619楼 得分:5
    up......
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • wgzaaa
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 15:28:1320楼 得分:5
    不要沉下去了,顺便标记
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • fcuandy
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-22 15:33:1521楼 得分:5
    j g f
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • prcgolf
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-25 14:07:1022楼 得分:5
    up
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • linguojin
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-25 15:03:2723楼 得分:5
    引用 22 楼 prcgolf 的回复:
    up
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • qqqwwwqw
    • 等级:
    • 可用分等级:
    • 总技术专家分:
    • 总技术专家分排名:
    发表于:2008-08-25 22:28:0224楼 得分:5
    up
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • hyde100
    • 等级:
    • 可用分等级:
    • 总技术分:
    • 总技术分排名:
    发表于:2008-09-17 16:01:5925楼 得分:0
    oo
    修改 删除 举报 引用 回复

    网站简介广告服务网站地图帮助联系方式诚聘英才English 问题报告
    北京创新乐知广告有限公司 版权所有 京 ICP 证 070598 号
    世纪乐知(北京)网络技术有限公司 提供技术支持
    Copyright © 2000-2008, CSDN.NET, All Rights Reserved