web-dev-qa-db-fra.com

Est-il possible d'interroger une table arborescente dans MySQL en une seule requête, à n'importe quelle profondeur?

Je pense que la réponse est non, mais j'adorerais que quelqu'un ait une idée de la façon d'explorer une structure arborescente à n'importe quelle profondeur dans SQL (MySQL), mais avec une seule requête

Plus précisément, étant donné une table arborescente (id, data, data, parent_id) et une ligne de la table, est-il possible d'obtenir tous les descendants (enfant/petit-enfant/etc), ou pour cela importe tous les ancêtres (parent/grand-parent/etc) sans savoir jusqu'où cela ira, en utilisant une seule requête?

Ou est-ce que l'utilisation d'une sorte de récursivité est nécessaire, où je continue à interroger plus profondément jusqu'à ce qu'il n'y ait pas de nouveaux résultats?

Plus précisément, j'utilise Ruby et Rails, mais je suppose que ce n'est pas très pertinent.

58
Cameron Booth

Oui, c'est possible, c'est ce qu'on appelle un arbre de précommande modifié Traversal, comme mieux décrit ici

Arbres et hiérarchies de Joe Celko dans SQL pour Smarties

Un exemple de travail (en PHP) est fourni ici

http://www.sitepoint.com/article/hierarchical-data-database/2/

37
Dave Cheney

Voici plusieurs ressources:

Fondamentalement, vous devrez faire une sorte de curseur dans une procédure stockée ou une requête ou créer une table de contiguïté. J'éviterais la récursivité en dehors de la base de données: selon la profondeur de votre arbre, cela pourrait devenir très lent/sommaire.

23
Aaron Jensen

La réponse de Daniel Beardsley n'est pas une mauvaise solution du tout lorsque les principales questions que vous posez sont "quels sont tous mes enfants" et "quels sont tous mes parents".

En réponse à Alex Weinstein, cette méthode entraîne en réalité moins de mises à jour des nœuds sur un mouvement parent que dans la technique Celko. Dans la technique de Celko, si un nœud de niveau 2 à l'extrême gauche se déplace sous un nœud de niveau 1 à l'extrême droite, alors presque tous les nœuds de l'arborescence doivent être mis à jour, plutôt que seulement les enfants du nœud.

Ce que je dirais cependant, c'est que Daniel stocke peut-être le chemin pour s'enraciner dans le mauvais sens.

Je les stockais pour que la requête soit

SELECT FROM table WHERE ancestors LIKE "1,2,6%"

Cela signifie que mysql peut utiliser un index sur la colonne 'ancêtres', ce qu'il ne pourrait pas faire avec un% en tête.

3
Kray

J'ai rencontré ce problème auparavant et j'ai eu une idée farfelue. Vous pouvez stocker un champ dans chaque enregistrement qui est une chaîne concaténée de ses identifiants directs d'ancêtres tout le chemin du retour à la racine.

Imaginez que vous ayez des enregistrements comme celui-ci (l'indentation implique la hiérarchie et les nombres sont id, ancêtres.

  • 1, "1"
    • 2, "2,1"
      • 5, "5,2,1"
      • 6, "6,2,1"
        • 7, "7,6,2,1"
        • 11, "11,6,2,1"
    • 3, "3,1"
      • 8, "8,3,1"
      • 9, "9,3,1"
      • 10, "10,3,1"

Ensuite, pour sélectionnez les descendants de id: 6, faites simplement cela

SELECT FROM table WHERE ancestors LIKE "%6,2,1"

La mise à jour de la colonne des ancêtres peut être plus difficile que cela ne vous en vaut la peine, mais c'est une solution réalisable dans n'importe quelle base de données.

2
Daniel Beardsley

La technique de Celko (ensembles imbriqués) est plutôt bonne. J'ai également utilisé une table de contiguïté avec les champs "ancêtre" et "descendant" et "distance" (par exemple, les enfants/parents directs ont une distance de 1, les petits-enfants/grands-parents ont une distance de 2, etc.).

Cela doit être maintenu, mais c'est assez facile à faire pour les insertions: vous utilisez une transaction, puis placez le lien direct (parent, enfant, distance = 1) dans la table, puis INSÉREZ IGNORE une SÉLECTION de parents et enfants existants en ajoutant des distances ( Je peux tirer le SQL quand j'en ai l'occasion), qui veut un index sur chacun des 3 champs pour les performances. Là où cette approche devient laide, c'est pour les suppressions ... vous devez essentiellement marquer tous les éléments qui ont été affectés, puis les reconstruire. Mais un avantage de cela est qu'il peut gérer des graphes acycliques arbitraires, alors que le modèle d'ensemble imbriqué ne peut faire que des hiérarchies droites (par exemple, chaque élément, sauf la racine, a un et un seul parent).

2
Jason S

Cela peut certainement être fait et ce n'est pas si compliqué pour SQL. J'ai répondu à cette question et fourni un exemple de travail en utilisant le code procédural mysql ici:

MySQL: Comment trouver des feuilles dans un nœud spécifique

Stand: Si vous êtes satisfait, vous devez marquer une des réponses comme acceptée.

1
guru_florida

SQL n'est pas un langage Turing Complete, ce qui signifie que vous ne pourrez pas effectuer ce type de boucle. Vous pouvez faire des choses très intelligentes avec SQL et les structures arborescentes, mais je ne peux pas penser à un moyen de décrire une ligne qui a un certain id "dans sa hiérarchie" pour une hiérarchie de profondeur arbitraire.

Votre meilleur pari est quelque chose qui ressemble à ce que @Dan a suggéré, qui est de simplement vous frayer un chemin à travers l'arbre dans un autre langage plus compétent. Vous pouvez réellement générer une chaîne de requête dans un langage général à l'aide d'une boucle, où la requête n'est qu'une série de jointures (ou sous-requêtes) alambiquées qui reflète la profondeur de la hiérarchie que vous recherchez. Ce serait plus efficace que le bouclage et les requêtes multiples.

1
Daniel Spiewak

J'ai utilisé la routine "With Emulator" décrite dans https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql (fournie par https: // stackoverflow .com/users/1726419/yossico ). Jusqu'à présent, j'ai obtenu de très bons résultats (en termes de performances), mais je n'ai pas une abondance de données ou un grand nombre de descendants à rechercher/à rechercher.

1
Big_Al_Tx