web-dev-qa-db-fra.com

Auto-jointures récursives

J'ai une table comments, qui peut être simplifiée comme suit:

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

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


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

15
Josh

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: https://stackoverflow.com/questions/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 le Parent = colonne) ici: https://stackoverflow.com/questions/192220/what-is-the-most-efficient-elegant-way-to-parse-a-flat-table-into-a-tree /

Bonne chance!

16
Valmoer

Cette conception de tableau est un "arborescence naïve" antipattern SQL comme décrit par Bill Karwin (en regardant 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)

7
redguy

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 à ce sujet:

En bref, les procédures stockées que j'ai faites effectuent une traversée de l'arborescence de précommande à l'aide du traitement de la file d'attente

  • GetParentIDByID
  • GetAncestry
  • GetFamilyTree

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

  • STEP01) Commencez par un parent_id dans une file d'attente
  • STEP02) Retirer la file d'attente suivante parent_id comme courant
  • STEP03) Mettez en file d'attente toutes les valeurs de id qui ont le parent_id
  • ÉTAPE04) Imprimez ou recueillez le commentaire
  • ÉTAPE05) Si la file d'attente n'est pas vide, passez à STEP02
  • ÉTAPE06) Vous avez terminé !!!

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

  • STEP01) Commencez avec un id dans une file d'attente
  • STEP02) Supprimez le id suivant comme courant
  • STEP03) Mettez en file d'attente le parent_id valeur de la id actuelle
  • ÉTAPE04) Imprimez ou recueillez le commentaire
  • ÉTAPE05) Si la file d'attente n'est pas vide, passez à STEP02
  • ÉTAPE06) Vous avez terminé !!!

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

Essaie !!!

5
RolandoMySQLDBA
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

2
Praveen Prasannan