来自:http://www.yesky.com/288/1880288.shtml
您可以从我以前的文章中回想起来, SQL书中有关树型结构的常见例子被称为邻接列表模式,它的表现形式如下:
CREATE TABLE Personnel
(emp CHAR(10) NOT NULL PRIMARY KEY,
boss CHAR(10) DEFAULT NULL REFERENCES Personnel(emp));
Personnel
emp boss
===================
'Albert' 'NULL'
'Bert' 'Albert'
'Chuck' 'Albert'
'Donna' 'Chuck'
'Eddie' 'Chuck'
'Fred' 'Chuck'
另外一种表现树型结构的方法就是将他们表示为嵌套集合。因为SQL是一种面向集合的语言,所以它是一种比你在大多数的教科书中看到的通常的邻接列表方式更好的模型。让我们定义如下所示的一个简单的personnel表,暂时忽略左(lft)列和右(rgt)列。在教科书中,问题通常是这样的:有一列代表着雇员,雇员中的一个是老板。在具有同样名字的图论技术以后,没有左右列的表就叫做邻接列表模型;结点的对是相互邻接的。
CREATE TABLE Personnel
(emp CHAR(10) NOT NULL PRIMARY KEY,
lft INTEGER NOT NULL UNIQUE CHECK (lft > 0),
rgt INTEGER NOT NULL UNIQUE CHECK (rgt > 1),
CONSTRAINT order_okay CHECK (lft < rgt) );
Personnel
emp lft rgt
======================
'Albert' 1 12
'Bert' 2 3
'Chuck' 4 11
'Donna' 5 6
'Eddie' 7 8
'Fred' 9 10
组织图如下所示,它是一个有向图:
Albert (1,12)
/ /
/ /
Bert (2,3) Chuck (4,11)
/ | /
/ | /
/ | /
/ | /
Donna (5,6) Eddie 7,8) Fred (9,10)
为了将树型结构表现为嵌套集合,可用椭圆来代替结点,然后在椭圆之间相互嵌套。根是最大的椭圆,它包含了所有其他的结点。叶子结点是最小的椭圆,它的里面什么也不包含,这种嵌套就显示了层次关系。左、右列就在这种嵌套中被表示出来。(我们在SQL中不能使用保留字LEFT和RIGHT。)
为了将图转换为嵌套集合模型,你可以想象有一个小虫沿着树爬行。小虫从顶部(即根)开始,沿着树做了一个完整的旅行。当他到达一个结点,就在那里放一个数字,表示他到过这里,同时增加他的计数器。每个结点都会有两个数字,一个是给左边的,一个是给右边的。计算机专业的学生都会认出这是一个修正的先序遍历的算法。
在T-SQL中实现它的代码就是直接了当地使用堆栈实现。首先,让我们将一些数据装进一个树型表中,然后创建一个堆栈表。随后我会解释这个堆栈是如何工作的。
--树代表着邻接模型
CREATE TABLE Tree
(emp CHAR(10) NOT NULL,
boss CHAR(10));
-- 插入测试数据
INSERT INTO Tree VALUES ('Albert', NULL);
INSERT INTO Tree VALUES ('Bert', 'Albert');
INSERT INTO Tree VALUES ('Chuck', 'Albert');
INSERT INTO Tree VALUES ('Donna', 'Chuck');
INSERT INTO Tree VALUES ('Eddie', 'Chuck');
INSERT INTO Tree VALUES ('Fred', 'Chuck');
--堆栈开始为空,随后保存嵌套集合模型
CREATE TABLE Stack
(stack_top INTEGER NOT NULL,
emp CHAR(10) NOT NULL,
lft INTEGER,
rgt INTEGER);
堆栈中的每一行都有一个嵌套集合对(左,右),结点值(emp),和一个整数(以一个整数代表当前栈顶)。当堆栈的顶部为正,则入栈,当堆栈的顶部是负的,则出栈。
虽然在T-SQL中有一些窍门可以用来表示堆栈,但下面这些算法是相当直接的,以下是我们所了解的:
1、我们要做(2 * (SELECT COUNT(*) FROM Tree))的操作来为每一个结点建立(左,右)对。为此我们需要一个计数器。
2、当一个节点进栈时,我们给他一个左边的数字,同时增加计数器的值
3、当一个节点出栈时,我们给他一个右边的数字,同时增加计数器的值。
4、我们从根开始,每一个结点都会出入栈一次,并且只有一次。
5、我们看到栈顶,并且把这个结点最小的下级入栈。
6、当栈顶的结点是左结点,或者是一个没有下级的结点时,就直接把它出栈。
下面是T-SQL中的代码:
CREATE TABLE Tree
(child CHAR(10) NOT NULL,
parent CHAR(10));
-- 插入测试数据
INSERT INTO Tree VALUES ('Albert', NULL);
INSERT INTO Tree VALUES ('Bert', 'Albert');
INSERT INTO Tree VALUES ('Chuck', 'Albert');
INSERT INTO Tree VALUES ('Donna', 'Chuck');
INSERT INTO Tree VALUES ('Eddie', 'Chuck');
INSERT INTO Tree VALUES ('Fred', 'Chuck');
DROP TABLE Stack;
CREATE TABLE Stack
(stack_top INTEGER NOT NULL,
child VARCHAR(10) NOT NULL,
lft INTEGER NOT NULL,
rgt INTEGER);
--你可以在栈顶和子列上创建可选择的索引
BEGIN
DECLARE @lft_rgt INTEGER, @stack_pointer INTEGER, @max_lft_rgt INTEGER;
SET @max_lft_rgt = 2 * (SELECT COUNT(*) FROM Tree);
INSERT INTO Stack
SELECT 1, child, 1, @max_lft_rgt
FROM Tree
WHERE parent IS NULL;
SET @lft_rgt = 2;
SET @Stack_pointer = 1;
DELETE FROM Tree
WHERE parent IS NULL;
--堆栈装满,以供使用
WHILE (@lft_rgt < @max_lft_rgt)
BEGIN
IF EXISTS (SELECT *
FROM Stack AS S1, Tree AS T1
WHERE S1.child = T1.parent
AND S1.stack_top = @stack_pointer)
BEGIN –当栈顶结点有下级的时候,将其压栈,并设置左值
INSERT INTO Stack
SELECT (@stack_pointer + 1), MIN(T1.child), @lft_rgt, NULL
FROM Stack AS S1, Tree AS T1
WHERE S1.child = T1.parent
AND S1.stack_top = @stack_pointer;
-- 从树中删除此行
DELETE FROM Tree
WHERE child = (SELECT child
FROM Stack
WHERE stack_top = @stack_pointer + 1);
SET @stack_pointer = @stack_pointer + 1;
END – 压栈
ELSE
BEGIN --出栈,并设置右值
UPDATE Stack
SET rgt = @lft_rgt,
stack_top = -stack_top
WHERE stack_top = @stack_pointer
SET @stack_pointer = @stack_pointer - 1;
END; -- 出栈
SET @lft_rgt = @lft_rgt + 1;
END; -- if
END; -- while
SELECT * FROM Stack ORDER BY lft;
Stack
stack_top emp lft rgt
-----------------------------
-1 Albert 1 12
-2 Bert 2 3
-2 Chuck 4 11
-3 Donna 5 6
-3 Eddie 7 8
-3 Fred 9 10
注意,剩余的栈顶数值是结点在原始树中深度的负值。同时还应注意到,原始树已经被这个过程给破坏了,所以你应该保存一下,并在临时表中用它的拷贝来代替。
引用 结束
理解:
在lft和rgt之间的节点就是这个节点的子节点
lft和rgt之间相差为1的这个节点没有子节点