Oracle FAQ Your Portal to the Oracle Knowledge Grid
HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US
 

Home -> Community -> Usenet -> comp.databases.theory -> Re: Nested set model with large gaps and spreads in the numbering

Re: Nested set model with large gaps and spreads in the numbering

From: John Gilson <jag_at_acm.org>
Date: Wed, 18 Sep 2002 07:32:07 GMT
Message-ID: <XZVh9.2110$GJ3.401908@twister.nyc.rr.com>


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
    update N1.lft = N1.lft - SG - PCG - SUM(ASG) - SUM(APCG),

           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.

IF EXISTS (SELECT *
           FROM RootNode
           WHERE value = @node AND
                 lft <> 1) AND

   @normalizeTree = 1
BEGIN
  DECLARE @rootGap INT
  SELECT @rootGap = lft - 1
  FROM RootNode
  UPDATE Tree
  SET lft = Tree.lft - @rootGap,

      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

Original text of this message

HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US