PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Wie kann ich alle IDs ab einer bestimmten ID rekursiv in einer Postgresql-Tabelle abrufen, die auf sich selbst verweist?

Verwenden Sie rekursiven allgemeinen Tabellenausdruck . Beginnen Sie immer mit der Wurzel und verwenden Sie ein Array von IDs, um Pfade für eine bestimmte id zu erhalten im WHERE Klausel.

Für id = 1 :

with recursive cte(id, parent, name, ids) as (
    select id, parent, name, array[id]
    from my_table
    where parent is null
union all
    select t.id, t.parent, concat(c.name, t.name, '/'), ids || t.id
    from cte c
    join my_table t on c.id = t.parent
)
select id, name 
from cte
where 1 = any(ids) and id <> 1

 id |         name          
----+-----------------------
  2 | /home/
  5 | /usr/
  6 | /usr/local/
  3 | /home/user/
  4 | /home/user/bin/
(5 rows)

Für id = 2 :

with recursive cte(id, parent, name, ids) as (
    select id, parent, name, array[id]
    from my_table
    where parent is null
union all
    select t.id, t.parent, concat(c.name, t.name, '/'), ids || t.id
    from cte c
    join my_table t on c.id = t.parent
)
select id, name 
from cte
where 2 = any(ids) and id <> 2

 id |         name          
----+-----------------------
  3 | /home/user/
  4 | /home/user/bin/
(2 rows)    

Bidirektionale Abfrage

Die Frage ist wirklich interessant. Die obige Abfrage funktioniert gut, ist aber ineffizient, da sie alle Baumknoten analysiert, selbst wenn wir nach einem Blatt fragen. Die leistungsfähigere Lösung ist eine bidirektionale rekursive Abfrage. Die innere Abfrage geht von einem bestimmten Knoten nach oben, während die äußere vom Knoten nach unten geht.

with recursive outer_query(id, parent, name) as (
    with recursive inner_query(qid, id, parent, name) as (
        select id, id, parent, name
        from my_table
        where id = 2        -- parameter
    union all
        select qid, t.id, t.parent, concat(t.name, '/', q.name)
        from inner_query q
        join my_table t on q.parent = t.id
    )
    select qid, null::int, right(name, -1)
    from inner_query
    where parent is null
union all
    select t.id, t.parent, concat(q.name, '/', t.name)
    from outer_query q
    join my_table t on q.id = t.parent
)
select id, name
from outer_query
where id <> 2;          -- parameter