Recursive query used for transitive closure

前端 未结 2 1792
无人共我
无人共我 2020-12-09 11:28

I\'ve created a simple example to illustrate transitive closure using recursive queries in PostgreSQL.

However, something is off with my recursive query. I\'m not fa

相关标签:
2条回答
  • 2020-12-09 11:37

    You have account 1 set as its own parent. If you set that account's parent to null you can avoid having that account as both the start and end node (the way your logic is setup you'll include a cycle but then won't add on to that cycle, which seems reasonable). It also looks a little nicer to change your final "path" column to something like case when parent_id is not null then path || parent_id else path end to avoid having the null at the end.

    0 讨论(0)
  • 2020-12-09 11:47

    You can simplify in several places (assuming acct_id and parent_id are NOT NULL):

    WITH RECURSIVE search_graph AS (
       SELECT parent_id, ARRAY[acct_id] AS path
       FROM   account
    
       UNION  ALL
       SELECT g.parent_id, sg.path || g.acct_id
       FROM   search_graph sg
       JOIN   account g ON g.acct_id = sg.parent_id 
       WHERE  g.acct_id <> ALL(sg.path)
       )
    SELECT path[1] AS child
         , path[array_upper(path,1)] AS parent
         , path
    FROM   search_graph
    ORDER  BY path;
    
    • The columns acct_id, depth, cycle are just noise in your query.
    • The WHERE condition has to exit the recursion one step earlier, before the duplicate entry from the top node is in the result. That was an "off-by-one" in your original.

    The rest is formatting.

    If you know the only possible circle in your graph is a self-reference, we can have that cheaper:

    WITH RECURSIVE search_graph AS (
       SELECT parent_id, ARRAY[acct_id] AS path, acct_id <> parent_id AS keep_going
       FROM   account
    
       UNION  ALL
       SELECT g.parent_id, sg.path || g.acct_id, g.acct_id <> g.parent_id
       FROM   search_graph sg
       JOIN   account g ON g.acct_id = sg.parent_id 
       WHERE  sg.keep_going
    )
    SELECT path[1] AS child
         , path[array_upper(path,1)] AS parent
         , path
    FROM   search_graph
    ORDER  BY path;
    

    SQL Fiddle.

    Note there would be problems (at least up to pg v9.4) for data types with a modifier (like varchar(5)) because array concatenation loses the modifier but the rCTE insists on types matching exactly:

    • Surprising results for data types with type modifier
    0 讨论(0)
提交回复
热议问题