| Oracle FAQ | Your Portal to the Oracle Knowledge Grid | |
Home -> Community -> Usenet -> comp.databases.theory -> Re: Nested set model with large gaps and spreads in the numbering
The algorithm I posted previously to solve this problem, while
correct, was unnecessarily constrained. I had applied shifting level
by level, i.e., before level N is shifted all levels before N are
fully shifted. This constraint has been removed leading to a cleaner
algorithm and code. Even though this solution is essentially the same
as the previous one, for completeness and ease of understanding I'll
make this posting self-contained.
The issue is to move the gaps between siblings in a tree to the right of the rightmost sibling while also maintaining the spread between each node's left and right values. Additionally, it would be useful to be able to set the root node's left value to 1 before left shifting so that the tree is put into "normal form". This would be useful if we wanted to present a subtree normalized. Furthermore, we might want to begin the left shifting at any node in the tree and not just the tree's root node. When working with a very large tree it could be much more efficient to left shift only certain subtrees, indicated by giving the subtree's root node.
Before we can left shift a tree we need to identify the gaps to
close. There are two types of gaps:
1. Between the left value of the oldest, i.e., leftmost, child CL and
the left value of its parent PL. We look for instances where CL - PL > 1. Let's call this kind of gap a ParentOldestChildGap. 2. Between the left value of a node S2L and the right value of the
sibling S1R to its immediate left. We look for instances where S2L - S1R > 1. Let's call this kind of gap a ConsecutiveSiblingGap.
Using this notion of gaps, the algorithm to left shift a tree or subtree rooted at node N is as follows:
declare N TreeNode -- root node
while(exists TreeNode N1 where N1 = N or N1 is a descendant of N and
N1 has a ConsecutiveSiblingGap or a ParentOldestChildGap)
begin
forall(TreeNode N1 where N1 = N or N1 is a descendant of N and
(N1 has a ConsecutiveSiblingGap SG; SG is 0 if none) or
(N1 has a ParentOldestChildGap PCG; PCG is 0 if none) or
forall(TreeNode N2 where N2 = N or N2 is a descendant of N
and N2 is an ancestor of N1 and N2 has an ancestor
ConsecutiveSiblingGap ASG or an ancestor
ParentOldestChildGap APCG; ASG and APCG are 0 if none))
begin
N1.rgt = N1.rgt - SG - PCG - SUM(ASG) - SUM(APCG)
from N1
end
end
Note that when left shifting a node with a gap, the same amount of shifting must be simultaneously applied to all descendants of this node so that the tree's ancestor-descendant relationship, represented by nested sets, is always maintained.
Below is T-SQL code, tested on SQL Server, that implements this algorithm followed by a few examples. Note that while iteration is used, no temp tables or extra columns are required and no local variables are needed in the loop.
First, let's show the DDL code and sample data.
CREATE TABLE Tree
(
value VARCHAR(10) NOT NULL PRIMARY KEY,
lft INT NOT NULL CHECK (lft > 0),
rgt INT NOT NULL CHECK (rgt > 0),
CHECK (rgt > lft)
)
INSERT INTO TREE
SELECT 'Albert', 100, 1200
UNION ALL
SELECT 'Bert', 111, 201
UNION ALL
SELECT 'Ernie', 250, 300
UNION ALL
SELECT 'Chuck', 400, 1100
UNION ALL
SELECT 'Donna', 501, 601
UNION ALL
SELECT 'Eddie', 701, 801
UNION ALL
SELECT 'Fred', 901, 1001
Next, let's look at some views that provide structural information about the tree.
Finally, the stored procedure that does the left shifting.
FROM RootNode
WHERE value = @node AND
lft <> 1) AND
rgt = Tree.rgt - @rootGap
END -- IF
Some examples might be illustrative. The tree we start with is
value lft rgt
Albert 100 1200
Bert 111 201
Ernie 250 300
Chuck 400 1100
Donna 501 601
Eddie 701 801
Fred 901 1001
If we want to left shift the entire tree we execute
EXEC ShiftLeft
so the resulting tree is
value lft rgt
Albert 100 1200
Bert 101 191
Ernie 192 242
Chuck 243 943
Donna 244 344
Eddie 345 445
Fred 446 546
If we want to shift the entire tree but start with a left value of 1 for the root node we execute
EXEC ShiftLeftNormalized
which results in the following tree
value lft rgt
Albert 1 1101
Bert 2 92
Ernie 93 143
Chuck 144 844
Donna 145 245
Eddie 246 346
Fred 347 447
Finally, we can just shift a subtree by giving that subtree's root node by executing
EXEC ShiftLeftSubtree 'Chuck'
which modifies the original table to
value lft rgt
Albert 100 1200
Bert 111 201
Ernie 250 300
Chuck 301 1001
Donna 302 402
Eddie 403 503
Fred 504 604
or
EXEC ShiftLeftSubtree 'Ernie'
value lft rgt
Albert 100 1200
Bert 111 201
Ernie 202 252
Chuck 400 1100
Donna 501 601
Eddie 701 801
Fred 901 1001
Regards,
jag
Received on Wed Sep 18 2002 - 02:32:07 CDT
![]() |
![]() |