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

Erkennen Sie doppelte Elemente in rekursivem CTE

Das Wort dep in der zweiten Abfrage (nach union ) ist nicht eindeutig. Tatsächlich wird es als die Spalte von rdeps interpretiert , nicht als Alias ​​von objectdependencies.

with recursive rdeps as (
  select dep
  from objectdependencies dep
  where dep.dependson = 4 -- starting point
  union all
  select dep -- this means r.dep
  from objectdependencies dep
  join rdeps r
    on (r.dep).id = dep.dependson
) select (dep).id from rdeps;

Aus diesem Grund erzeugt die Abfrage eine Endlosschleife. Sie können dies korrigieren, indem Sie den Alias ​​ändern:

with recursive rdeps as (
  select dep
  from objectdependencies dep
  where dep.dependson = 4 -- starting point
  union all
  select objectdep
  from objectdependencies objectdep
  join rdeps r
    on (r.dep).id = objectdep.dependson
) select (dep).id from rdeps;

 id 
----
  1
  2
  3
  1
  2
  1
(6 rows)    

Oder besser, indem Sie einfach Spalten verwenden, wie der liebe Gott es beabsichtigt hat:

with recursive rdeps as (
    select id, dependson
    from objectdependencies
    where dependson = 4
union all
    select d.id, d.dependson
    from objectdependencies d
    join rdeps r
    on r.id = d.dependson
) 
select *
from rdeps;

Die erste Abfrage in der Frage ist alles, was Sie in einfachem SQL tun können, da keine Kommunikation zwischen verschiedenen (parallelen) Zweigen stattfindet, die durch eine rekursive Abfrage generiert werden. Bei einem funktionalen Ansatz können Sie eine temporäre Tabelle als gemeinsamen Speicher für alle Zweige verwenden. Die Funktion könnte so aussehen:

create or replace function rec_function(int)
returns void language plpgsql as $$
declare
    i int;
begin
    for i in
        select id
        from objectdependencies
        where dependson = $1
    loop
        if not exists(
            select from temp_table 
            where id = i)
        then
            insert into temp_table values(i);
            perform rec_function(i);
        end if;
    end loop;
end $$;

Verwendung:

create temp table temp_table(id int);

select rec_function(4);

select *
from temp_table;