sql >> Base de Datos >  >> RDS >> PostgreSQL

Detectar elementos duplicados en CTE recursivo

La palabra dep en la segunda consulta (después de union ) es ambiguo. De hecho se interpreta como la columna de rdeps , no como un alias de 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;

Esta es la razón por la que la consulta crea un bucle sin fin. Puede corregir esto cambiando el alias:

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)    

O mejor, simplemente usando columnas, como Dios quiso:

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;

La primera consulta en la pregunta es todo lo que puede hacer en SQL simple, ya que no hay comunicación entre diferentes ramas (paralelas) generadas por una consulta recursiva. En un enfoque funcional, puede usar una tabla temporal como una tienda común para todas las sucursales. La función puede verse así:

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 $$;

Uso:

create temp table temp_table(id int);

select rec_function(4);

select *
from temp_table;