Est-il possible de faire un appel récursif de la requête SQL?
-
09-06-2019 - |
Question
J'ai une table similaire à ceci:
CREATE TABLE example (
id integer primary key,
name char(200),
parentid integer,
value integer);
Je peux utiliser la parentid champ d'organiser les données dans une structure en arbre.
Voici maintenant le peu que je ne peux pas savoir.Étant donné un parentid, est-il possible d'écrire une instruction SQL pour ajouter tous les champs de la valeur en vertu de cette parentid et une boucle en bas de la branche de l'arbre ?
Mise à JOUR: Je suis l'aide de posgreSQL de sorte que la fantaisie MS-SQL fonctionnalités ne sont pas disponibles pour moi.En tout cas, j'aimerais que ce soit traitée comme un générique SQL question.
BTW, je suis très impressionné d'avoir 6 réponses dans les 15 minutes de poser la question!Aller débordement de pile!
La solution
Il ya quelques façons de faire ce que vous avez besoin dans PostgreSQL.
Si vous pouvez installer des modules, regardez la tablefunc contrib.Il a un connectby() la fonction qui gère la traversée des arbres. http://www.postgresql.org/docs/8.3/interactive/tablefunc.html
Découvrez également les ltree contrib, que vous pouvez adapter votre table à utiliser: http://www.postgresql.org/docs/8.3/interactive/ltree.html
Ou vous pouvez parcourir l'arborescence de vous-même avec des fonctions PL/PGSQL.
Quelque chose comme ceci:
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);
Autres conseils
Voici un exemple de script utilisant l'expression de table commune:
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
Le script ci-dessus crée un "virtuel" table appelée sumthis
qui a des colonnes id
et val
.Il est défini comme le résultat de deux sélectionne fusionné avec union all
.
Première select
obtient la racine (where id = :selectedid
).
Deuxième select
suit les enfants de la précédente, les résultats de manière itérative jusqu'à ce qu'il n'y a rien de revenir.
Le résultat peut ensuite être traitée comme un tableau normal.Dans ce cas, le val de la colonne est résumée.
Depuis la version 8.4, PostgreSQL a requête récursive de soutien pour les expressions de table communes à l'aide de la norme SQL WITH
la syntaxe.
Si vous voulez une solution portable qui fonctionne sur n'importe quel ANSI SQL-92 SGBDR, vous aurez besoin d'ajouter une nouvelle colonne à votre table.
Joe Celko est l'auteur original de la Ensembles Imbriqués approche pour le stockage des hiérarchies dans SQL.Vous pouvez Google "imbriqués définit la" hiérarchie pour comprendre plus au sujet de l'arrière-plan.
Ou vous pouvez simplement renommer parentid à leftid et ajouter un rightid.
Voici ma tentative de résumer Imbriquée Ensembles, qui va tomber lamentablement court car je ne suis pas Joe Celko:SQL est un langage basé sur, et de la proximité du modèle (stockage parent ID) n'est PAS un jeu basé sur la représentation d'une hiérarchie.Il n'est donc pas pure, basés sur la méthode de la requête d'un schéma d'adjacence.
Cependant, la plupart des grandes plates-formes ont introduit des extensions au cours des dernières années pour faire face à ce problème précis.Donc, si quelqu'un répond avec une Postgres-solution spécifique, utilisez-le par tous les moyens.
D'une manière standard de faire une requête récursive dans SQL
sont récursives CTE
. PostgreSQL
les prend en charge depuis 8.4
.
Dans les versions antérieures, vous pouvez écrire un ensemble récursif-de retour de la fonction:
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)
Voir cet article:
Si vous utilisez SQL Server 2005, il y a une façon vraiment cool pour ce faire à l'aide d'Expressions de Table Communes.
Il prend tous les gruntwork de la création d'une table temporaire, et fondamentalement vous permet de tout faire avec, juste un AVEC et un SYNDICAT.
Voici un bon tutoriel:
http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html
l'utilisation d'un expression de table commune.
Peut vouloir indiquer c'est SQL Server 2005 ou au-dessus seulement. Dale Ragan
voici un article sur la récursivité par SqlTeam sans expressions de table communes.
Le code suivant compile et il est testé 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';
La condition "de l'enfant <> parent" est nécessaire dans mon cas parce que les noeuds point à eux-mêmes.
Amusez-vous :)
Oracle a "START" et "se CONNECTER EN"
select
lpad(' ',2*(level-1)) || to_char(child) s
from
test_connect_by
start with parent is null
connect by prior child = parent;
Juste une brève de côté, bien que la question a été répondue très bien, il convient de noter que si nous traitons cela comme un:
générique SQL question
puis le SQL de mise en œuvre est assez simple, comme SQL'99 permet linéaire de la récursivité dans le cahier des charges (même si je ne crois pas le Sgbdr mettre en œuvre la norme entièrement) par le biais de la WITH RECURSIVE
l'énoncé.Donc, à partir d'un point de vue théorique, nous pouvons faire de ce droit maintenant.
Aucun des exemples travaillé sur OK pour moi donc je l'ai résolu comme ceci:
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;
est-ce SQL Server?Ne pourrait-on pas écrire une TSQL procédure stockée qui boucle et les syndicats les résultats de l'ensemble?
Je suis également intéressé si il y a un SQL seule façon de le faire bien.À partir de l'bits je me souviens de mes bases de données géographiques de classe, il devrait y être.
Je pense que c'est plus facile dans SQL 2008 avec HierarchyID
Si vous avez besoin de stocker arbitraire graphiques, et pas seulement des hiérarchies, de pousser des Postgres sur le côté et essayer un graphique de base de données tels que AllegroGraph:
Tout dans le graphique de la base de données est stockée comme un triple (nœud source, edge, nœud cible) et il vous donne le soutien de première classe pour la manipulation de la structure graphique et d'interrogation à l'aide de SQL comme langage.
Il ne s'intègre pas bien avec quelque chose comme Hibernate ou ORM de Django, mais si vous êtes sérieux au sujet de graphe structures (et pas seulement des hiérarchies comme l'Ensemble Imbriqué modèle vous donne) check it out.
Je crois aussi que l'Oracle a finalement ajouté un support pour de vrai Graphiques dans leurs derniers produits, mais je suis étonné de voir qu'il a fallu si longtemps, beaucoup de problèmes pourraient bénéficier de ce modèle.