? ? ? 构造一棵树,只要有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;?开始图:

?
运行后的结果

?