您的位置:首页 > 数据库

带有层号的先根遍历树存储基于plsql的代码实现

2017-07-13 23:19 519 查看
本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!

摘要:本文介绍了带有层号的先根遍历树存储基于plsql的代码实现,本文中涉及的代码编写于2014年3月。另外,本文提供了测试表创建语句以及插入节点、获取直接子节点、获取自身及子孙节点、获取根到指定节点的路径、删除节点等方法的存储过程源码与测试样例。

1 存储方式介绍

带有层号的先根遍历树存储方式的主要思想是通过记录先根遍历中第一次访问节点时的次序号(以下称左值)与回溯时第二次访问的次序号(以下称右值)来维护树型结构的层次关系。由先根遍历的概念可知,子节点的左值必然大于父节点左值,而右值必然小于父节点的右值。再结合排序操作就可以很容易的在不递归的情况下对树型数据进行各种查询操作。另外,在记录左值、右值的基础上,该方式还维护了节点的层号,以降低查询直接(父)子节点的时间复杂度。

带有层号的先根遍历树存储结构如表1-1所示。

表1-1 带有层号的先根遍历树的存储结构



2 建表语句

带有层号的先根遍历树在Oracle下的建表语句如下。

CREATE TABLE TREE
(
NODENAME   NVARCHAR2(50) NOT NULL,
LEVELNUM  NUMBER(8) NOT NULL,
LEFTVALUE   NUMBER(8) NOT NULL,
RIGHTVALUE  NUMBER(8) NOT NULL );

COMMENT ON TABLE TREE IS '带有层号的先根遍历树'
;
COMMENT ON COLUMN TREE.NODENAME   IS '节点名'
;
COMMENT ON COLUMN TREE.LEVELNUM  IS '层号,从1开始'
;
COMMENT ON COLUMN TREE.LEFTVALUE   IS '先根遍历左值'
;
COMMENT ON COLUMN TREE.RIGHTVALUE  IS '先根遍历右值'
;

ALTER TABLE TREE ADD CONSTRAINT PK_NODENAME
PRIMARY KEY (NODENAME)
;


3 插入节点

3.1 实现

插入节点存储过程代码如下:

procedure InsertNode --插入节点
(parentName in nvarchar2, --父节点名,插入根时为空
nodeName  in nvarchar2 --节点名
) is
parentRight TREE.RIGHTVALUE%type; --父节点右值
parentLevel TREE.LEVELNUM%type; --父节点层次
begin
if parentName is null then
insert into TREE values (nodeName, 1, 1, 2);
else
--获取父节点右值
select RIGHTVALUE, LEVELNUM
into parentRight, parentLevel
from TREE
where NODENAME = parentName;
update TREE
set LEFTVALUE = LEFTVALUE + 2
where LEFTVALUE > parentRight - 1;
update TREE
set RIGHTVALUE = RIGHTVALUE + 2
where RIGHTVALUE > parentRight - 1;
insert into TREE
values
(nodeName, parentLevel + 1, parentRight, parentRight + 1);
end if;
end InsertNode;


3.2 测试

比如,我们要创建如图3-1所示的树。



图3-1 测试用例树结构图

要创建上面的树,可以调用以下示例代码:

begin
-- Call the procedure
tree_package.InsertNode('', 'A');
tree_package.InsertNode('A', 'B');
tree_package.InsertNode('A', 'C');
tree_package.InsertNode('A', 'D');
tree_package.InsertNode('C', 'E');
tree_package.InsertNode('C', 'F');
tree_package.InsertNode('C', 'G');
end;


执行后测试代码后,表记录被更新为如表3-1所示的状态。

表3-1 插入节点后的表记录



4 获取直接子节点

4.1 实现

获取直接子节点存储过程代码如下:

procedure GetChildren --获得子节点
(parentName IN NVARCHAR2, --父节点名
children  OUT RETCURSOR --子节点名
) is
parentLeft  TREE.LEFTVALUE%type; --父节点左值
parentRight TREE.RIGHTVALUE%type; --父节点右值
parentLevel TREE.LEVELNUM%type;
begin
select LEFTVALUE, RIGHTVALUE, LEVELNUM
into parentLeft, parentRight, parentLevel
from TREE
where NODENAME = parentName;
open children for
select NODENAME
from TREE
where LEFTVALUE between parentLeft and parentRight
and LEVELNUM = parentLevel + 1
order by LEFTVALUE asc;
end GetChildren;


4.2 测试

比如我们要获取节点A的直接子节点,调用GetChildren 后,得到的游标如表4-1所示。

表4-1 获取直接子节点返回结果游标



5 获取当前节点及子孙节点

5.1 实现

获取当前节点及子孙节点存储过程代码如下:

procedure GetDescendants --获取子孙节点
(parentName IN NVARCHAR2, --父节点名
descendants  OUT RETCURSOR --子孙节点名
)is
parentLeft  TREE.LEFTVALUE%type; --父节点左值
parentRight TREE.RIGHTVALUE%type; --父节点右值
begin
select LEFTVALUE, RIGHTVALUE
into parentLeft, parentRight
from TREE
where NODENAME = parentName;
open descendants for
select NODENAME
from TREE
where LEFTVALUE between parentLeft and parentRight
order by LEFTVALUE asc;
end GetDescendants;


5.2 测试

比如我们要获取节点A及其子孙节点,调用GetDescendants后,得到的游标如表5-1所示。

表5-1 获取当前节点及其子孙节点返回结果游标



6 获取根到指定节点的路径

6.1 实现

获取根到指定节点的路径存储过程代码如下:

procedure GetNodePath --获得节点路径
(node IN NVARCHAR2, --节点名
nodePath OUT RETCURSOR --节点路径
) is
nodeLeft  TREE.LEFTVALUE%type; --节点左值
nodeRight TREE.RIGHTVALUE%type; --节点右值
begin
select LEFTVALUE, RIGHTVALUE
into nodeLeft, nodeRight
from TREE
where NODENAME = node;
open nodePath for
select NODENAME
from TREE
where LEFTVALUE <= nodeLeft
and RIGHTVALUE >= nodeRight
order by LEFTVALUE asc;
end GetNodePath;


6.2 测试

比如我们要获取根到节点G的路径,调用GetNodePath后,得到的游标如表6-1所示。

表6-1 根到指定节点返回结果游标



7 删除节点

7.1 实现

删除节点的存储过程代码如下:

procedure DeleteNode --删除节点及其子节点
(node IN NVARCHAR2 --节点名
) is
nodeLeft     TREE.LEFTVALUE%type; --节点左值
nodeRight    TREE.RIGHTVALUE%type; --节点右值
delNodeCount NUMBER; --删除的节点数
begin
select LEFTVALUE, RIGHTVALUE
into nodeLeft, nodeRight
from TREE
where NODENAME = node;
delNodeCount := (nodeRight - nodeLeft+1)/2;
delete from TREE
where LEFTVALUE >= nodeLeft
and RIGHTVALUE <= nodeRight;
update TREE
set LEFTVALUE = LEFTVALUE - delNodeCount * 2
where LEFTVALUE > nodeLeft;
update TREE
set RIGHTVALUE = RIGHTVALUE - delNodeCount * 2
where RIGHTVALUE > nodeRight;
end DeleteNode;


7.2 测试

比如,我们要删除节点C,则C的子节点E、F、G将一并删除。删除后的表记录状态如表7-1所示。

表7-1 删除节点后的表记录

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息