Re: Nested set model with large gaps and spreads in the numbering
Date: Wed, 18 Sep 2002 07:32:07 GMT
Message-ID: <XZVh9.2110$GJ3.401908_at_twister.nyc.rr.com>
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.
- Tree's root node CREATE VIEW RootNode AS SELECT T1.value, T1.lft, T1.rgt FROM Tree AS T1 WHERE NOT EXISTS (SELECT * FROM Tree AS T2 WHERE T2.lft < T1.lft AND T2.rgt > T1.rgt)
- Define tree parent-child relationship CREATE VIEW Children AS SELECT T1.value AS parent, T2.value AS child, T1.lft AS lftParent, T1.rgt AS rgtParent, T2.lft AS lftChild, T2.rgt AS rgtChild FROM Tree AS T1 INNER JOIN Tree AS T2 ON T2.lft > T1.lft AND T2.rgt < T1.rgt AND NOT EXISTS (SELECT * FROM Tree AS T3 WHERE T3.lft > T1.lft AND T3.lft < T2.lft AND T3.rgt < T1.rgt AND T3.rgt > T2.rgt)
- Define tree ancestor-descendant relationship CREATE VIEW Descendants AS SELECT T1.value AS ancestor, T2.value AS descendant FROM Tree AS T1 INNER JOIN Tree AS T2 ON T2.lft > T1.lft AND T2.rgt < T1.rgt
- Consecutive siblings with a gap > 0 CREATE VIEW ConsecutiveSiblingGaps AS SELECT C1.child AS lftChild, C2.child AS rgtChild, C2.lftChild - C1.rgtChild - 1 AS gap FROM Children AS C1 INNER JOIN Children AS C2 ON C1.parent = C2.parent AND C1.rgtChild < C2.lftChild - 1 AND NOT EXISTS (SELECT * FROM Children AS C3 WHERE C3.parent = C1.parent AND C3.lftChild > C1.rgtChild AND C3.rgtChild < C2.lftChild)
- Gap between parent and oldest child, i.e., leftmost child, > 0 CREATE VIEW ParentOldestChildGaps AS SELECT C1.parent AS parent, C1.child AS child, C1.lftChild - C1.lftParent - 1 AS gap FROM Children AS C1 WHERE NOT EXISTS (SELECT * FROM Children AS C2 WHERE C2.parent = C1.parent AND C2.lftChild < C1.lftChild) AND C1.lftParent < C1.lftChild - 1
Finally, the stored procedure that does the left shifting.
- Procedures to left-shift tree
- All gaps are moved rightward
- All spreads remain the same CREATE PROCEDURE ShiftLeftHelper _at_node VARCHAR(10), -- root node of tree or subtree to begin shifting _at_normalizeTree BIT = 0 -- normalize tree? AS BEGIN
FROM RootNode WHERE value = _at_node AND lft <> 1) AND
_at_normalizeTree = 1
BEGIN
DECLARE _at_rootGap INT
SELECT _at_rootGap = lft - 1
FROM RootNode
UPDATE Tree
SET lft = Tree.lft - _at_rootGap,
rgt = Tree.rgt - _at_rootGap
END -- IF
- While any gaps exist at or below provided root node, left shift nodes
- with gaps and all of their descendants by the necessary amounts WHILE EXISTS (SELECT * FROM ConsecutiveSiblingGaps AS G WHERE G.rgtChild = _at_node OR EXISTS (SELECT * FROM Descendants AS D WHERE D.ancestor = _at_node AND D.descendant = G.rgtChild)) OR EXISTS (SELECT * FROM ParentOldestChildGaps AS G WHERE G.child = _at_node OR EXISTS (SELECT * FROM Descendants AS D WHERE D.ancestor = _at_node AND D.descendant = G.child)) BEGIN UPDATE Tree SET lft = Tree.lft - COALESCE((SELECT gap FROM ConsecutiveSiblingGaps WHERE rgtChild = Tree.value), (SELECT gap FROM ParentOldestChildGaps WHERE child = Tree.value), 0) - COALESCE((SELECT SUM(G.gap) FROM ConsecutiveSiblingGaps AS G INNER JOIN Descendants AS D1 ON D1.ancestor = G.rgtChild AND D1.descendant = Tree.value AND (G.rgtChild = _at_node OR EXISTS (SELECT * FROM Descendants AS D2 WHERE D2.ancestor = _at_node AND D2.descendant = G.rgtChild))), 0) - COALESCE((SELECT SUM(G.gap) FROM ParentOldestChildGaps AS G INNER JOIN Descendants AS D1 ON D1.ancestor = G.child AND D1.descendant = Tree.value AND (G.child = _at_node OR EXISTS (SELECT * FROM Descendants AS D2 WHERE D2.ancestor = _at_node AND D2.descendant = G.child))), 0), rgt = Tree.rgt - COALESCE((SELECT gap FROM ConsecutiveSiblingGaps WHERE rgtChild = Tree.value), (SELECT gap FROM ParentOldestChildGaps WHERE child = Tree.value), 0) - COALESCE((SELECT SUM(G.gap) FROM ConsecutiveSiblingGaps AS G INNER JOIN Descendants AS D1 ON D1.ancestor = G.rgtChild AND D1.descendant = Tree.value AND (G.rgtChild = _at_node OR EXISTS (SELECT * FROM Descendants AS D2 WHERE D2.ancestor = _at_node AND D2.descendant = G.rgtChild))), 0) - COALESCE((SELECT SUM(G.gap) FROM ParentOldestChildGaps AS G INNER JOIN Descendants AS D1 ON D1.ancestor = G.child AND D1.descendant = Tree.value AND (G.child = _at_node OR EXISTS (SELECT * FROM Descendants AS D2 WHERE D2.ancestor = _at_node AND D2.descendant = G.child))), 0) WHERE (Tree.value = _at_node OR EXISTS (SELECT * FROM Descendants WHERE descendant = Tree.value AND ancestor = _at_node)) AND (EXISTS (SELECT * FROM ConsecutiveSiblingGaps AS G WHERE G.rgtChild = Tree.value) OR EXISTS (SELECT * FROM ConsecutiveSiblingGaps AS G INNER JOIN Descendants AS D1 ON D1.ancestor = G.rgtChild AND D1.descendant = Tree.value AND (G.rgtChild = _at_node OR EXISTS (SELECT * FROM Descendants AS D2 WHERE D2.ancestor = _at_node AND D2.descendant = G.rgtChild))) OR EXISTS (SELECT * FROM ParentOldestChildGaps AS G WHERE G.child = Tree.value) OR EXISTS (SELECT * FROM ParentOldestChildGaps AS G INNER JOIN Descendants AS D1 ON D1.ancestor = G.child AND D1.descendant = Tree.value AND (G.child = _at_node OR EXISTS (SELECT * FROM Descendants AS D2 WHERE D2.ancestor = _at_node AND D2.descendant = G.child)))) END -- WHILE END -- PROCEDURE
- Left-shift complete tree CREATE PROCEDURE ShiftLeft AS DECLARE _at_root VARCHAR(10) SELECT _at_root = value FROM RootNode IF _at_root IS NOT NULL EXEC ShiftLeftHelper _at_root
- Left-shift complete tree with the root's left value set to 1, i.e.,
- placed in normal form CREATE PROCEDURE ShiftLeftNormalized AS DECLARE _at_root VARCHAR(10) SELECT _at_root = value FROM RootNode IF _at_root IS NOT NULL EXEC ShiftLeftHelper _at_root, 1
- Only left-shift subtree rooted at the given node CREATE PROCEDURE ShiftLeftSubtree _at_node VARCHAR(10) AS IF EXISTS (SELECT * FROM Tree WHERE value = _at_node) EXEC ShiftLeftHelper _at_node
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 - 09:32:07 CEST