Search code examples
sqltreehierarchical-datarecursive-queryhierarchical-query

Hierarchical SQL Queries: Best SQL query to obtain the whole branch of a tree from a [nodeid, parentid] pairs table given the end node id


Is there any way to send a recursive query in SQL?

Given the end node id, I need all the rows up to the root node (which has parentid = NULL) ordered by level. E.g. if I have something like:

nodeid | parentid
a      | NULL    
b      | a       
c      | b       

after querying for end_node_id = c, I'd get something like:

nodeid | parentid | depth
a      | NULL     | 0
b      | a        | 1
c      | b        | 2

(Instead of the depth I can also work with the distance to the given end node)

The only (and obvious) way I could come up with is doing a single query per row until I reach the parent node.

Is there a more efficient way of doing it?


Solution

  • Ended up with the following solutions (where level is the distance to the end node)

    Oracle, using hierarchical queries (thanks to the info provided by @Mureinik):

    SELECT     IDCATEGORY, IDPARENTCATEGORY, LEVEL
    FROM       TNODES
    START WITH IDCATEGORY=122
    CONNECT BY IDCATEGORY = PRIOR IDPARENTCATEGORY;
    

    Example using a view so it boils down to a single standard SQL query (requires >= 10g):

    CREATE OR REPLACE VIEW VNODES AS 
    SELECT CONNECT_BY_ROOT IDCATEGORY "IDBRANCH", IDCATEGORY, IDPARENTCATEGORY, LEVEL AS LVL
    FROM TNODES 
    CONNECT BY IDCATEGORY = PRIOR IDPARENTCATEGORY;
    
    SELECT * FROM VNODES WHERE IDBRANCH = 122 ORDER BY LVL ASC;
    

    http://sqlfiddle.com/#!4/18ba80/3

    Postgres >= 8.4, using a WITH RECURSIVE Common Table Expression query:

    WITH RECURSIVE BRANCH(IDPARENTCATEGORY, IDCATEGORY, LEVEL) AS (
        SELECT IDPARENTCATEGORY, IDCATEGORY, 1 AS LEVEL FROM TNODES WHERE IDCATEGORY = 122
      UNION ALL
        SELECT p.IDPARENTCATEGORY, p.IDCATEGORY, LEVEL+1
        FROM BRANCH pr, TNODES p
        WHERE p.IDCATEGORY = pr.IDPARENTCATEGORY
      )
    SELECT IDCATEGORY,IDPARENTCATEGORY, LEVEL
    FROM BRANCH
    ORDER BY LEVEL ASC
    

    Example using a view so it boils down to a single standard SQL query:

    CREATE OR REPLACE VIEW VNODES AS 
    WITH RECURSIVE BRANCH(IDBRANCH,IDPARENTCATEGORY,IDCATEGORY,LVL) AS (
      SELECT IDCATEGORY AS IDBRANCH, IDPARENTCATEGORY, IDCATEGORY, 1 AS LVL FROM TNODES
      UNION ALL
        SELECT pr.IDBRANCH, p.IDPARENTCATEGORY, p.IDCATEGORY, LVL+1
        FROM BRANCH pr, TNODES p
        WHERE p.IDCATEGORY = pr.IDPARENTCATEGORY
      )
    SELECT IDBRANCH, IDCATEGORY, IDPARENTCATEGORY, LVL
    FROM BRANCH;
    
    SELECT * FROM VNODES WHERE IDBRANCH = 122 ORDER BY LVL ASC;
    

    http://sqlfiddle.com/#!11/42870/2