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

je pense que la réponse est non, mais j'en serais ravie. n'importe qui a une idée sur la façon de ramper une structure d'arbre à n'importe quelle profondeur en SQL (MySQL), mais avec une seule requête

plus spécifiquement, étant donné une table arborescente structurée (id, data, data, parent_id), et une rangée dans la table, est-il possible d'obtenir tous les descendants (enfant / petit-enfant / etc), ou d'ailleurs tous les ancêtres (parent/grand-parent/etc) sans savoir jusqu'où il sera allez, à l'aide d'une seule requête?

ou utilise-t-on une sorte de récursion, où je continue à chercher plus profondément jusqu'à ce qu'il n'y ait pas de nouveaux résultats?

spécifiquement, J'utilise Ruby et Rails, mais je devine que ce n'est pas très pertinent.

57
demandé sur Vertexwahn 2008-10-04 09:42:04

9 réponses

Oui, c'est possible, c'est un pré-commande modifié de traversée de L'arbre, comme le décrit le mieux ici

Joe Celko des Arbres et des Hiérarchies dans SQL pour les Smarties

un exemple de travail (en PHP) est fourni ici

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

37
répondu Dave Cheney 2008-10-04 06:55:15

voici plusieurs ressources:

fondamentalement, vous aurez besoin de faire une sorte de curseur dans une procédure stockée ou une requête ou construire une table de contiguïté. J'avais évitez la récursion en dehors de la base de données: selon la profondeur de votre arbre, cela pourrait devenir vraiment lent/vague.

23
répondu Aaron Jensen 2011-08-21 20:18:32

la réponse de Daniel Beardsley n'est pas si mauvaise que ça quand les principales questions que vous posez sont 'qu'est-ce que sont tous mes enfants' et 'qu'est-ce que sont tous mes parents'.

en réponse à Alex Weinstein, cette méthode entraîne en fait moins de mises à jour des noeuds sur un mouvement parent que dans la technique Celko. Dans la technique de Celko, si un noeud de niveau 2 à l'extrême gauche passe sous un noeud de niveau 1 à l'extrême droite, alors à peu près tous les noeuds de l'arbre doivent être mis à jour., plutôt que les enfants du noeud.

ce que je dirais, cependant, C'est que Daniel stocke peut-être le chemin de retour à root dans le mauvais sens.

je les stockerais pour que la requête soit

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

cela signifie que mysql peut faire usage d'un index sur la colonne 'ancêtres', qu'il ne serait pas en mesure de faire avec un %dirigeant.

3
répondu Kray 2012-03-05 15:07:14

je suis tombé sur ce problème avant et j'ai eu une idée folle. Vous pouvez stocker un champ dans chaque enregistrement qui est une chaîne concaténée de ses ancêtres directs jusqu'à la racine.

Imaginez que vous aviez des documents comme celui-ci (indentation implique l'hérarchie et les nombres sont des 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"

puis à sélectionnez les descendants de id: 6 , faites juste ceci

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

garder la colonne des ancêtres à jour pourrait être plus difficile que ce qu'elle vaut pour vous, mais c'est une solution réalisable dans n'importe quel DB.

2
répondu Daniel Beardsley 2008-10-04 06:45:59

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

cela doit être maintenu, mais est assez facile à faire pour les inserts: vous utilisez une transaction, puis mettez le lien direct (parent, enfant, distance=1) dans la table, puis insérez ignorer une sélection d'existant parents&enfants en ajoutant des distances (je peux tirer vers le haut le SQL lorsque j'ai une chance), qui se veut un index sur chacune des 3 champs pour la performance. Si cette approche devient laid est pour les suppressions... vous devez essentiellement marquer tous les éléments qui ont été touchés et puis les reconstruire. Mais un avantage de ceci est qu'il peut manipuler des graphes acycliques arbitraires, alors que le modèle d'ensemble imbriqué peut seulement faire des hiérarchies droites (par exemple chaque élément sauf que la racine a un et un seul parent).

2
répondu Jason S 2008-12-08 19:43:21

SQL n'est pas un langage complet Turing, ce qui signifie que vous n'allez pas être en mesure d'effectuer ce genre de boucle. Vous pouvez faire des choses très intelligentes avec des structures de SQL et d'arbre, mais je ne peux pas penser à une façon de décrire une rangée qui a un certain id "dans sa hiérarchie" pour une hiérarchie de profondeur arbitraire.

votre meilleur pari est quelque chose du genre de ce que @Dan a suggéré, qui est de simplement travailler votre chemin à travers l'arbre dans un autre langage plus compétent. Vous peut en fait générer une chaîne de requête dans un langage universel en utilisant une boucle, où la requête est juste une série alambiquée de jointures (ou de sous-requêtes) qui reflète la profondeur de la hiérarchie que vous recherchez. Ce serait plus efficace que la boucle et les requêtes multiples.

1
répondu Daniel Spiewak 2008-10-04 06:07:57

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 de procédure mysql ici:

MySQL: comment trouver des feuilles dans un noeud spécifique

Booth: si vous êtes satisfait, vous devez noter l'une des réponses acceptées.

1
répondu guru_florida 2017-05-23 11:46:17

j'ai utilisé la routine "avec émulateur" décrite dans https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql (fourni par https://stackoverflow.com/users/1726419/yossico ). Jusqu'à présent, j'ai obtenu de très bons résultats (du point de vue de la performance), mais je n'ai pas une abondance de données ou un grand nombre de descendants à chercher à travers/pour.

1
répondu Big_Al_Tx 2017-05-23 12:17:11

vous allez presque certainement vouloir employer une certaine récursion pour cela. Et si vous faites cela, alors il serait trivial (en fait plus facile) d'obtenir l'arbre entier plutôt que des morceaux de celui-ci à une profondeur fixe.

en pseudo-code très rugueux vous voulez quelque chose du genre:

getChildren(parent){
    children = query(SELECT * FROM table WHERE parent_id = parent.id)
    return children
}

printTree(root){
    print root
    children = getChildren(root)
    for child in children {
        printTree(child)
    }
}

bien que dans la pratique vous voudriez rarement faire quelque chose comme ça. Il sera plutôt inefficace puisqu'il fait une demande pour chaque rang dans la table, donc ce ne sera raisonnable que pour les petites tables, ou les arbres qui ne sont pas nichés trop profondément. Pour être honnête, dans les deux cas, vous voudrez probablement pour limiter la profondeur.

cependant, étant donné la popularité de ces types de structure de données, il peut très bien y avoir quelques trucs MySQL pour vous aider avec cela, spécifiquement pour réduire le nombre de requêtes que vous devez faire.

Edit: après y avoir pensé, il est très peu logique de faire tous ces requête. Si vous lisez la table entière de toute façon, alors vous pouvez simplement slurp le tout en RAM-en supposant qu'il est assez petit!

-1
répondu Dan 2008-10-04 05:54:58