Auto-jointures récursives

15

J'ai un commentstableau, qui peut être simplifié comme suit:

comments
=======
id
user_id
text
parent_id

parent_idest nullable, mais peut être une clé pour son commentaire parent.


Maintenant, comment puis-je selecttous les descendants d'un commentaire spécifique?
Les commentaires peuvent être de plusieurs niveaux plus bas ...

Josh
la source

Réponses:

16

Les requêtes hiérarchiques , comme ces requêtes récursives sont connues, ne sont pas prises en charge pour MySQL.

Ils sont cependant pris en charge dans Oracle, Microsoft SQL Server, DB2 et PostgreSQL, entre autres.

Si vous avez besoin d'une solution de contournement, vous pouvez trouver une astuce dynamique (et donc potentiellement dangereuse) ici: /programming/8104187/mysql-hierarchical-queries

Vous pouvez également trouver une discussion sur la façon de stocker des données hiérarchiques avec d'autres modèles qu'avec une liste d'adjacence (c'est-à-dire la colonne Parent ) ici: /programming/192220/what-is-the-most-efficient- façon-élégante-d'analyser-une-table-plate-dans-un-arbre /

Bonne chance!

Valmoer
la source
Je me demande en quoi cette solution dans votre deuxième lien peut être dangereuse. Pourriez-vous l'expliquer? Sinon, bienvenue sur le site!
dezso
3
@dezso: Quoth l'artisan de la requête lui-même, Quassnoi " * Ce n'est pas sûr pour la mise à niveau * parce que MySQL ne définit pas clairement le comportement des variables de session. Cependant, c'est le seul moyen de traiter les listes de contiguïté en temps opportun dans la requête ." Dangereux a peut-être été un mot trop fort ( potentiellement instable ?) Mais je préfère faire preuve de prudence (en plus, je connais mieux Oracle que MySQL, donc je voulais être extrêmement prudent). Merci pour l'accueil, au fait! Je suis un rôdeur de longue date sur le réseau SE, et j'ai décidé qu'il était temps de rembourser un peu.
Valmoer
2
La syntaxe WITH [RECURSIVE] est désormais prise en charge à partir de mysql 8.0. dev.mysql.com/doc/refman/8.0/en/with.html
ClearCrescendo
6

Cette conception de table est un "arborescence naïve" antipattern SQL tel que décrit par Bill Karwin (à partir de la diapositive 48 dans sa présentation SQL Antipatterns Strike Back ). Le problème avec cette conception est précisément la difficulté d'obtenir tous les descendants (ou parents) d'un nœud. Puisque vous utilisez MySQL, vous ne pouvez pas utiliser d'expressions de table communes (l'instruction WITH et son modificateur RECURSIVE) présentes dans d'autres SGBDR.

Il vous reste:

  • utiliser une implémentation alternative de la structure de données hiérarchique (les réponses à cette question pourraient être une bonne référence à ce sujet)
  • créer des requêtes d'auto-jointure avec une limite de profondeur. Pour une profondeur = 5, vous pouvez utiliser quelque chose comme:

    SELECT *
    FROM comments AS c1
      JOIN comments AS c2 ON (c2.parent_id = c1.id)
      JOIN comments AS c3 ON (c3.parent_id = c2.id)
      JOIN comments AS c4 ON (c4.parent_id = c3.id)
      JOIN comments AS c5 ON (c5.parent_id = c4.id)
  • utiliser un SGBDR qui prend en charge WITH RECURSIVE (bien que ce ne soit probablement pas une option pour la plupart des gens)

redguy
la source
2
Je ne suis pas d'accord avec Bill Karwin ici. Le modèle d'adjacence n'est pas un anti-modèle. Avec un SGBD moderne qui prend en charge les requêtes récursives (Oracle le supporte depuis plus de 20 ans), un tel modèle est très efficace pour récupérer et mettre à jour.
a_horse_with_no_name
5

MySQL ne prend pas en charge les requêtes récursives telles que celle dont vous avez besoin.

Ce que j'ai fait il y a quelque temps, j'ai écrit des procédures stockées qui fournissent le modèle pour le faire.

Plutôt que de réinventer la roue, je vais vous donner les liens vers mes précédents articles sur ce sujet:

En bref, les procédures stockées que j'ai faites font la pré-commande de la traversée de l'arbre en utilisant le traitement de la file

  • GetParentIDByID
  • GetAncestry
  • GetFamilyTree

Parent à tous les enfants (comme la procédure stockée GetFamilyTree)

  • STEP01) Commencez avec un parent_iddans une file d'attente
  • STEP02) Supprimer le suivant parent_idcomme courant
  • STEP03) Mettre en file d'attente toutes les idvaleurs qui ont le courantparent_id
  • ÉTAPE04) Imprimez ou recueillez le commentaire
  • STEP05) Si la file d'attente n'est pas vide, goto STEP02
  • ÉTAPE06) Vous avez terminé !!!

De l'enfant à tous les parents (comme la procédure stockée GetAncestry)

  • STEP01) Commencez avec un iddans une file d'attente
  • STEP02) Supprimer le suivant idcomme courant
  • STEP03) Mettre en file d'attente la parent_idvaleur du courantid
  • ÉTAPE04) Imprimez ou recueillez le commentaire
  • STEP05) Si la file d'attente n'est pas vide, goto STEP02
  • ÉTAPE06) Vous avez terminé !!!

Veuillez consulter les procédures stockées dans mes autres articles pour voir la mise en œuvre.

Essaie !!!

RolandoMySQLDBA
la source
2
SELECT  group_concat(@id :=
        (
        SELECT  id
        FROM    comments
        WHERE   parent_id = @id
        )) AS comment
FROM    (
        SELECT  @id := 1
        ) vars
STRAIGHT_JOIN
        comments
WHERE   @id IS NOT NULL

violon

Praveen Prasannan
la source