当前位置: 代码迷 >> SQL >> plsql 树节点挪动
  详细解决方案

plsql 树节点挪动

热度:106   发布时间:2016-05-05 14:54:29.0
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;
?

开始图:

?

运行后的结果

?

  相关解决方案