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