PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Detectar itens duplicados em CTE recursiva


A palavra dep na segunda consulta (depois de union ) é ambíguo. Na verdade, é interpretado como a coluna de rdeps , não como um 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;

É por isso que a consulta cria um loop infinito. Você pode corrigir isso alterando o 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)    

Ou melhor, apenas usando colunas, como o bom Deus pretendia:
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;

A primeira consulta na pergunta é tudo o que você pode fazer em sql simples, pois não há comunicação entre diferentes ramos (paralelos) gerados por uma consulta recursiva. Em uma abordagem funcional, você pode usar uma tabela temporária como uma loja comum a todas as filiais. A função pode ficar assim:
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;