? ? ? 构造一棵树,只要有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;?
开始图:
?
运行后的结果
?