in reply to (OT?) Recursive sql queries?

However that required a lot of perl glue and I wondered if there was any more elegant method to retrieve the tree from the database?

You might want to consider using an alternate nested set representation of trees, rather than storing a parent ID for each node. Using this representation you can get a subtree with a single SQL query, no recursion necessary.

Replies are listed 'Best First'.
Re: Re: (OT?) Recursive sql queries?
by dws (Chancellor) on Feb 26, 2004 at 19:15 UTC
Re: Re: (OT?) Recursive sql queries?
by dragonchild (Archbishop) on Feb 26, 2004 at 12:58 UTC
    Interesting article. Do you have any others? It was a teaser, but didn't provide methods for maintaining said list.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      The second part of the article actually talks about maintaining the tables as far as deleting sub-trees. And the third part of the article gets into inserting sub-trees. It gets more involved and complex as it goes, but if you are reading more than you are writing, this method seems pretty cool.

      -stvn
        if you are reading more than you are writing, this method seems pretty cool.

        Actually, most writing functions are easier too ;-) Update operations on subtrees and leaf nodes can usually be implemented in a single SQL statement.

        Insertion is really the only vaguely complex/expensive operation - and that's still just three SQL statements in a basic implementation.

        The only downside is that inserts can be expensive compared to just storing the parent ID. With the implementation Celko outlined in the article inserting a node is O(N).

        Still the average case isn't that bad. You can also make inserts less expensive on average by using increments larger than 1 and inserting in the "gaps" until a global renumbering becomes necessary - but since this makes the selects more complex, and most systems read/update a lot more than they insert, it's rarely worthwhile.

Re: Re: (OT?) Recursive sql queries?
by BUU (Prior) on Feb 26, 2004 at 20:18 UTC
    Hrm, that looks interesting, the sql is certainly nice and simple enough, but I have to wonder, how do you maintain it? What happens if, after you've got all your nice lft and rgt columns set up, you want to insert a new node someplace?

    After thinking about it for a bit, I think the biggest win is just to store 2 ids, an "ultimate parent" and a "sub parent", then I can just get all my nodes in one query and munge them in to a tree in perl.
      Hrm, that looks interesting, the sql is certainly nice and simple enough, but I have to wonder, how do you maintain it? What happens if, after you've got all your nice lft and rgt columns set up, you want to insert a new node someplace?

      It's not hard. There previously mentioned Intelligent Enterprise article covers one possible method. It's worth a read. You can do lots of useful things (aggregate reports, deleting subtrees, etc.) very quickly over in SQL land without having to bring stuff out of the database and munge it into a hierarchy on the Perl side.

      After thinking about it for a bit, I think the biggest win is just to store 2 ids, an "ultimate parent" and a "sub parent", then I can just get all my nodes in one query and munge them in to a tree in perl.

      This, of course, depends on your definition of "biggest win" ;-)

      This way you have to have an id for every level of subtree that you want to refer to which means a change to the DB schema if you change the hierarchy.

        Well, the accessing sql is refreshingly simple, but the updating code
        BEGIN DECLARE right_most_sibling INTEGER; SET right_most_sibling = (SELECT rgt FROM Personnel WHERE emp = :your_boss); UPDATE Personnel SET lft = CASE WHEN lft > right_most_sibling THEN lft + 2 ELSE lft END, rgt = CASE WHEN rgt >= right_most_sibling THEN rgt + 2 ELSE rgt END WHERE rgt >= right_most_sibling; INSERT INTO Personnel (emp, lft, rgt) VALUES ('New Guy', right_most_sibling, (right_most_sibling + 1)) END;
        Is rather scary and I'm not even sure my database (mysql) supports those.