plsql 树节点移动

      构造一棵树,只要有parentId这个字段,就可以构造出了。但若只有parentId字段,想得到这个节点的父节点的父节点。。或子节点的子节点。。的信息,就只能不断的递归循环,这样显然很吃力不讨好。所以实际中我们会利用一些冗余字段保存更多的信息,更直接的信息,比如 isleaf,levelNum,layerCode,layerIds....

 

我们这个帖子要讨论的就是针对levelNum,layerCode,layerIds 这三个字段来展开的

 

方式1:layerCode 形式   这种发式的结构是这样的:

        中国 ->广东 ->广州 ->天河区

                |            |           |->番禺区

        |            |->深圳

                |->福建 ->厦门

 

   对应的layerCode 为: {中国:001} [{广东:001001},{福建:001002}] {广州:001001001}{厦门:001002001}

                              [{天河区:001001001001},{番禺区:001001001002}]

 

现在如果将广州移动到福建下,则对应的编码应该变为

{广州:001002002}  [{天河区:001002002001},{番禺区:001002002002}]

 

具体编码见如下图:

 

现在的我们要把广州移动到福建下,这样就需要级联的改变所有广州之下的layerCode 和levelNum这些值,如下存储过程实现这些功能:

create or replace procedure MoveTree1(fromId in varchar2, toId in varchar2) is
       v_fromCode        varchar2(400);--发起节点原编码
       v_toCode          varchar2(400);--目标节点编码
       v_moveCode        varchar2(400);--发起节点新编码
       v_tempCode        varchar2(400);
       n_countChild      number(3);--目标节点的子节点数量
       n_tempNum         number(4);
       n_levelBetween    number(2);  
       --获取发起节点之下的所有子节点的游标
       CURSOR cur IS 
              SELECT t2.* FROM local_tree t1 , local_tree t2 where t2.node_code like t1.node_code || '%' and t1.obj_id = fromId;
begin
       select t.node_code into v_fromCode from local_tree t where t.obj_id = fromId;
       select t.node_code into v_toCode from local_tree t where t.obj_id = toId;
       n_levelBetween := (length(v_fromCode)-length(v_toCode)) / 3 -1;
       DBMS_OUTPUT.put_line('发起 code=' || v_fromCode || '目标 code=' || v_toCode);
       DBMS_OUTPUT.put_line(n_levelBetween);
       
       select count(*) into n_countChild from local_tree  where parent_id = toId;
       
       --目标节点没有子节点,则直接 + 001
       if n_countChild < 1 then
          v_moveCode := v_toCode || '001';
          DBMS_OUTPUT.put_line('目标无子节点 结果 code=' || v_moveCode);
       --目标节点存在子节点,获取最大子节点code,并+1
       else
          select max(t.node_code) into v_tempCode from local_tree t where t.parent_id = toId;
          DBMS_OUTPUT.put_line('最大子节点 code=' || v_tempCode);
          
          --获取最大子节点层级编码+1
          v_tempCode := substr(v_tempCode,(length(v_tempCode)-2),3);
          n_tempNum  := to_number(v_tempCode) + 1001;
          v_tempCode := to_char(n_tempNum);
          v_tempCode :=substr(v_tempCode,2,3);
          
          v_moveCode := v_toCode || v_tempCode;
          
          
          DBMS_OUTPUT.put_line('目标有子节点 结果 code=' || v_moveCode);
          
          --循环设置发起节点所有子节点的新code
          n_tempNum :=0;
          for cur_tree in cur LOOP
              begin
                  if cur_tree.node_code <> v_fromCode then
                      --以发起节点的code为坐标,得到此节点的子节点的编码
                      v_tempCode := substr(cur_tree.node_code,length(v_fromCode)+1,(length(cur_tree.node_code)-length(v_fromCode)));
                      --将子节点的相对编码 加上 发起节点新的code编码
                      v_tempCode := v_moveCode || v_tempCode;
                  else
                      v_tempCode := v_moveCode;
                      --修改发起点的父节点为目标节点
                      update local_tree t set t.parent_id = toId where t.obj_id = fromId;
                  end if;
              
                  DBMS_OUTPUT.put_line('子节点' || cur_tree.node_name ||' code=' || v_tempCode );
                  
                  --修改子节点记录的编码
                  update local_tree t set t.node_code = v_tempCode ,t.node_level = t.node_level - n_levelBetween where  t.obj_id = cur_tree.obj_id;
                  n_tempNum := n_tempNum+1;
              
                  exception
                      when others then
                          DBMS_OUTPUT.put_line('出现异常');
                          n_tempNum := n_tempNum-1;
              end;
          end LOOP;
       end if;
       
       commit;
       --counts := n_tempNum;
       DBMS_OUTPUT.put_line('共更新' || to_char(n_tempNum) || '条记录');
end MoveTree1;
 

调用此存储过程:

call movetree1('33EF2F8E-39D9-4389-8201-77867294F047','CD15177B-133B-4746-A47F-78F6D42167A2')

 

输出信息:

发起 code=001014001目标 code=001013

0

最大子节点 code=001013010

目标有子节点 结果 code=001013011

子节点广州 code=001013011

子节点天河区 code=001013011001

子节点番禺区 code=001013011002

子节点岗顶 code=001013011001001

子节点华南理工 code=001013011001001001

子节点华南农业 code=001013011001001002

子节点华师大 code=001013011001001003

共更新7条记录

 

结果截图:

 

缺点:目前移动到目标节点只是将目标节点子节点中的最大layerCode值+1,在子节点多,移动频繁的情况下,可能3位长度可能会不够(当然我们可以设置长些)。若改进为如果子节点的layercoder不是连续的,中间有空缺的编码,我们找到这些空缺的编码,作为被移动后的编码,如果是连续的采用最大值+1的方式。此方式缺点是:不能通过节点的layercode表示同级节点间的先后关系。

 

 

方式2,layerIds ,保存所有父节点的ID,并用分隔符分割,组成结构与layerCode一样,只是值有ID组成。具体需求和layerCode的一样,就不再重复了,直接贴代码和结果图

代码:

create or replace procedure moveTree2(fromId in varchar2, toId in varchar2) is
       v_fromCode        varchar2(4000);--发起节点原编码
       v_toCode          varchar2(4000);--目标节点编码
       v_moveCode        varchar2(4000);--发起节点新编码
       v_tempCode        varchar2(4000);
       CURSOR cur IS 
              SELECT t2.* FROM local_tree t1 , local_tree t2 where t2.node_code like t1.node_code || '%' and t1.obj_id = fromId;
begin
       select t.node_code into v_fromCode from local_tree t where t.obj_id = fromId;
       select t.node_code into v_toCode from local_tree t where t.obj_id = toId;
       
       v_moveCode := v_toCode || toId || '|';
       
       DBMS_OUTPUT.put_line(v_moveCode);
       
       for cur_tree in cur LOOP
           begin
               if fromId = cur_tree.obj_id then
                  v_tempCode := v_moveCode;
                  DBMS_OUTPUT.put_line('子节点 ' || cur_tree.node_name || ' code= ' || v_tempCode);
                  update local_tree t set t.parent_id = toId where t.obj_id = cur_tree.obj_id;
               else
                  v_tempCode := substr(cur_tree.node_code,(length(v_fromCode)+1),(length(cur_tree.node_code)-length(v_fromCode)));
                  DBMS_OUTPUT.put_line(v_tempCode);
                  v_tempCode := v_moveCode || v_tempCode;
                  DBMS_OUTPUT.put_line('子节点 ' || cur_tree.node_name || ' code= ' || v_tempCode);
               end if;
               update local_tree t set t.node_code = v_tempCode where t.obj_id = cur_tree.obj_id;        
           exception
                when others then
                    DBMS_OUTPUT.put_line('出现异常');
           end;
       end LOOP;
end moveTree2;
 

开始图:

 

运行后的结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值