É possível fazer uma recursiva consulta SQL?
-
09-06-2019 - |
Pergunta
Eu tenho uma tabela semelhante a este:
CREATE TABLE example (
id integer primary key,
name char(200),
parentid integer,
value integer);
Eu posso usar o parentid campo para organizar dados em uma estrutura de árvore.
Agora aqui está a pouco eu não posso trabalhar fora.Dado um parentid, é possível escrever uma instrução SQL para adicionar todos os campos de valor em que parentid e recurse para baixo o ramo da árvore ?
ATUALIZAÇÃO: Eu estou usando o posgreSQL, de modo a fantasia MS-SQL funcionalidades não estão disponíveis para mim.Em qualquer caso, eu gostaria de esta ser tratada como um genérico do SQL questão.
BTW, estou muito impressionado ter 6 respostas dentro de 15 minutos de fazer a pergunta!Ir estouro de pilha!
Solução
Existem algumas maneiras de fazer o que você precisa no PostgreSQL.
Se você pode instalar módulos, olhar para o tablefunc contrib.Ele tem uma connectby() função que manipula atravessando as árvores. http://www.postgresql.org/docs/8.3/interactive/tablefunc.html
Confira também a ltree contrib, que você pode adaptar a sua tabela para usar: http://www.postgresql.org/docs/8.3/interactive/ltree.html
Ou você pode percorrer a árvore de si mesmo com um PL/PGSQL função.
Algo como isto:
create or replace function example_subtree (integer)
returns setof example as
'declare results record;
child record;
begin
select into results * from example where parent_id = $1;
if found then
return next results;
for child in select id from example
where parent_id = $1
loop
for temp in select * from example_subtree(child.id)
loop
return next temp;
end loop;
end loop;
end if;
return null;
end;' language 'plpgsql';
select sum(value) as value_sum
from example_subtree(1234);
Outras dicas
Aqui está um script de exemplo usando a expressão de tabela comum:
with recursive sumthis(id, val) as (
select id, value
from example
where id = :selectedid
union all
select C.id, C.value
from sumthis P
inner join example C on P.id = C.parentid
)
select sum(val) from sumthis
O script acima cria um 'virtual' tabela chamada sumthis
que tem colunas id
e val
.Ele é definido como o resultado de dois seleciona mesclado com union all
.
Primeira select
fica a raiz (where id = :selectedid
).
Segunda select
segue os filhos dos resultados anteriores iterativamente até que não há nada de voltar.
O resultado final pode então ser processado como uma mesa normal.Neste caso, o val coluna é somado.
Desde a versão 8.4, o PostgreSQL tem consulta recursiva de apoio para expressões de tabela comuns utilizando o padrão SQL WITH
a sintaxe.
Se você quer uma solução portátil que irá funcionar em qualquer ANSI SQL-92 RDBMS, você vai precisar adicionar uma nova coluna à tabela.
Joe Celko é o autor original do Conjuntos Aninhados abordagem para o armazenamento de hierarquias no SQL.Você pode Google "aninhadas conjuntos de" hierarquia para entender mais sobre o plano de fundo.
Ou você pode apenas mudar o nome para parentid leftid e adicionar um rightid.
Aqui está a minha tentativa de resumir Conjuntos Aninhados, que vai cair muito curto, porque eu não sou Joe Celko:O SQL é um conjunto de base de língua, e adjacência (modelo de armazenamento de IDENTIFICAÇÃO do pai) NÃO é um conjunto de base de representação de uma hierarquia.Portanto, não há pura conjunto baseado no método para consultar uma adjacência esquema.
No entanto, a maioria das principais plataformas de ter introduzido extensões nos últimos anos para lidar com este problema concreto.Então, se alguém responde com uma Postgres-solução específica, use-o por todos os meios.
Uma forma padrão de fazer uma consulta recursiva em SQL
são recursivas CTE
. PostgreSQL
suporta-los desde que 8.4
.
Em versões anteriores, você pode escrever uma recursiva do conjunto-retorno de função:
CREATE FUNCTION fn_hierarchy (parent INT)
RETURNS SETOF example
AS
$$
SELECT example
FROM example
WHERE id = $1
UNION ALL
SELECT fn_hierarchy(id)
FROM example
WHERE parentid = $1
$$
LANGUAGE 'sql';
SELECT *
FROM fn_hierarchy(1)
Consulte este artigo:
Se seu usando o SQL Server 2005, não é uma forma muito legal para fazer isso usando Expressões de Tabela Comuns.
Leva todo o gruntwork a criação de uma tabela temporária e, basicamente, permite que você faça isso tudo com apenas uma COM e uma UNIÃO.
Aqui está um bom tutorial:
http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html
usar um expressão de tabela comum.
Pode querer indicar que este é o SQL Server 2005 ou superior apenas. Dale Ragan
aqui está um artigo na recursão por SqlTeam sem expressões de tabela comuns.
O seguinte código é compilado e testado OK.
create or replace function subtree (bigint) returns setof example as $$ declare results record; entry record; recs record; begin select into results * from example where parent = $1; if found then for entry in select child from example where parent = $1 and child parent loop for recs in select * from subtree(entry.child) loop return next recs; end loop; end loop; end if; return next results; end; $$ language 'plpgsql';
A condição de "criança <> pai" é necessário no meu caso, porque nós aponte para si.
Divirta-se :)
A Oracle tem "COMEÇAM COM" e "CONECTAR"
select
lpad(' ',2*(level-1)) || to_char(child) s
from
test_connect_by
start with parent is null
connect by prior child = parent;
Assim como uma breve lado, embora a pergunta tenha sido respondida muito bem, deve ser observado que, se nós tratamos isso como um:
SQL genérico pergunta
em seguida, a implementação de SQL é bastante simples, como o SQL'99 permite recursão linear, em que a especificação (embora eu acredite que não RDBMSs implementar o padrão totalmente), através da WITH RECURSIVE
instrução.Assim, a partir de uma perspectiva teórica, podemos fazer isso agora.
Nenhum dos exemplos trabalhou OK para mim assim que eu tiver resolvido assim:
declare results record; entry record; recs record; begin for results in select * from project where pid = $1 loop return next results; for recs in select * from project_subtree(results.id) loop return next recs; end loop; end loop; return; end;
é este o SQL Server?Você não poderia escrever um TSQL procedimento armazenado que efetua loops através de sindicatos e os resultados?
Eu também estou interessado se houver um SQL-única maneira de fazê-lo embora.De bits eu lembrar dos meus bancos de dados geográficos de classe, não deve ser.
Eu acho que é mais fácil no SQL 2008 com O tipo de dados HierarchyID
Se você precisar armazenar arbitrário gráficos, não apenas hierarquias, você pode empurrar o Postgres para o lado e tentar um gráfico de banco de dados, tais como AllegroGraph:
Tudo no gráfico de banco de dados é armazenado como uma tripla (nó de fonte, borda, nó de destino) e ele dá a você suporte de primeira classe para manipular o grafo e consulta-lo usando um SQL como linguagem.
Ele não se integra bem com algo como o Hibernate ou Django ORM, mas se você é sério sobre o gráfico de estruturas (não só hierarquias como o Aninhadas modelo de Conjunto dá-lhe) o check-out.
Eu também acredito que a Oracle tem finalmente adicionado um suporte para real Gráficos em seus mais recentes produtos, mas estou impressionado é tomado tanto tempo, muitos problemas poderiam se beneficiar deste modelo.