Other tree representations in SQL
Date: 3 Dec 2002 15:27:37 -0800
Message-ID: <c0d87ec0.0212031527.7ca335bf_at_posting.google.com>
Vadim Tropashko suggested that we could represent a tree structure in SQL by storing a traversal sequence number and the level of each node to represent a hierarchy. This would be an alternative to my nested sets model
Tree Traversals are usually associated with binary trees in the literature because the three types of traversals can be written in very neat, clean recursive procedures. But they can be generalized to any tree by moving from the leftmost to the rightmost subtree. There are three ways to traverse a tree -- preorder, inorder and postorder.
- (SELECT MAX(T1.seq) AS parent FROM Tree AS T1,Tree AS T2 WHERE T1.seq < T2.seq AND T1.level = T2.level -1 AND T1.node = :my_child); 2) Finding a subtree rooted at a particular node is more complex. The leftmost child of a node is easy to find; it is the sequence number of the parent plus one. The problem is establishing a rightmost child in a subtree.
You know that all the children will have a greater sequence number than the root and a greater level number. But at the same time, you do not want to get the nodes in other subtrees that are siblings to the right of the root node.
The way you know that a sequence number belongs to another subtree is that while the sequence number increased, the level number decreased. For example, if we want to see the subtree rooted at ('c', 3, 2) in the sample table, we know that the subtree must have greater sequence number, thus:
('d', 4, 3)
('e', 5, 3)
('f', 6, 3)
('g', 7, 2) <==
('h', 8, 3)
('i', 9, 3)
Since 'c' is at level 2, we only need to look at nodes with a greater level number, thus
('d', 4, 3)
('e', 5, 3)
('f', 6, 3)
('h', 8, 3)
('i', 9, 3)
But this leaves 'h' and 'i', which are in another subtree under 'g' and clearly wrong. What we want to find is the rightmost sequence number in the target subtree. This number also happens to be the first sequence number that jumps back up to the same or a higher level as the root. Look at how 'g' is back on level 2.
But there is another problem. When you look at the rightmost subtree, there is no node to the right, so we have to use the MAX(seq) in the whole tree. This leads us to this query.
SELECT :my_root, T1.*
FROM Tree AS T1, Tree AS T2
WHERE T2.node = :my_root
AND T2.seq < T1.seq
AND T1.level > T2.level
AND T1.seq
<= COALESCE((SELECT MIN(T3.seq)
FROM Tree AS T3
WHERE T3.level = T2.level
AND T3.seq > T2.seq),
(SELECT MAX(seq) FROM Tree));
