Re: Adjacency list to Nested set model statement

From: Kendall <kendallwillets_at_yahoooooo.com>
Date: Thu, 28 Feb 2002 00:40:44 -0800
Message-ID: <u7rr8d15fc76df_at_news.supernews.com>


In article <c0d87ec0.0202271438.12a87592_at_posting.google.com>, "--CELKO--" <71062.1056_at_compuserve.com> wrote:

> Here is the code; can you make it better?
>
> /* Stack holds the nested sets model; it is the push down stack for the
> tree traversal. Would an index or key help here? */
>
>

Do you really need a stack here? Traversing a tree consists of iteratively taking one of three moves, in this order of priority: down, right, up. Given the last node visited, it seems simple enough to figure out what the next one should be.

Maybe something like this:

create table node(

         node_id integer primary key, -- unique id
         parent_id integer null,  -- null only at root
         nodenum integer null  -- traversal ordinal, initially null
         maxchild null  -- the max. nodenum for subtree
         )

/*

The traversal order will be depth first, ordering siblings by node_id.

Each node will be given a nodenum when first encountered (on the way down/across). On the way up, nodenum's are already set, but maxchild can be brought up from below (although it's redundant IMHO).

*/

declare _at_last_node_visited integer;
declare _at_last_nodenum integer;

set _at_last_node_visited = @root_node -- assume this is passed in

set _at_last_nodenum = 1

update node set nodenum = 1 where node_id = _at_root_node

set _at_did_something = 1;

while ( _at_did_something = 1 ) /* 0 if no more moves */ begin

    set _at_did_something = 0

    /* down - look for unvisited child */     if exists (select * from tree where parent_id = _at_last_node_visited

               and nodenum is null )
         begin
          set _at_did_something = 1;

          /* get leftmost child */
          set _at_next_node = (select min(node_id) from node c
            where  c.parent_id = _at_last_nodenum
            )

          set _at_last_nodenum = @last_nodenum + 1;

          update node set nodenum = _at_last_nodenum 
          where node_id = _at_next_node;

          set _at_last_node_visited = @next_node;
         end  /* down  */

    /* across - look for right sibling */     else if exists (select * from node c, node c2

                    where c.node_id = _at_last_node_visited
                    and c.parent_id = c2.parent_id
                    and c2.node_id > c.node_id )
         begin
              set _at_did_something = 1;

              set _at_next_node = 
                 (select min(c2.node_id)
                 from node c, node c2
                 where c.node_id = _at_last_node_visited
                 and c.parent_id = c2.parent_id
                 and c2.node_id > c.node_id )

                set _at_last_nodenum = @last_nodenum + 1;

                update node set nodenum = _at_last_nodenum 
                where node_id = _at_next_node;

                set _at_last_node_visited = @next_node;
         end  /* across */

     /* up - set maxchild */
     else if exists (select * from node c
                     where c.node_id = _at_last_node_visited
                     and c.parent_id is not null )
         begin
              set _at_did_something = 1;
              set _at_next_node = 
                     (select parent_id from node c
                     where c.node_id = _at_last_node_visited
                     and c.parent_id is not null )

              /* don't increment _at_last_nodenum */

              /* set maxchild for node; nodenum should already be set */
              update node set maxchild  = _at_last_nodenum 
                where node_id = _at_next_node;

              set _at_last_node_visited = @next_node;
 
         end /* up */

end /* while */

This is pretty brutal, untested coding, but I think the idea is visible somewhere in there. I could probably get it a lot cleaner with a bit of rework, but I'm sleepy.

Feedback *is* appreciated.

Thanks,

Kendall Received on Thu Feb 28 2002 - 09:40:44 CET

Original text of this message