web-dev-qa-db-fra.com

Query récursif dans MySQL à l'aide de la procédure stockée et du curseur

Je développe notre système utilisateur/groupe pour permettre des groupes dynamiques composés d'autres groupes. J'ai trois tables users, groups et relationships.

Pour la simplicité, disons utilisateurs ne contient qu'un seul champ user_id, et groupes ne contient que group_id. relationships utilise trois champs: user_id, group_id, related_group_id Pour représenter le groupe à la fois au groupe et groupe au groupe .

Une hiérarchie de groupe peuplée de manière dynamique serait quelque chose comme:

Utilisateurs A, B, C appartient au groupe 1 et utilisateurs D, E, F appartient au groupe 2. Tous sont représentés dans les relations tableau comme (user_id, group_id):

A,1
B,1
C,1
D,2
E,2
F,2

Le groupe dynamique 3 aurait deux groupe au groupe Records de relations (group_id, related_group_id):

3,1
3,2

Pour illustrer mon problème, mieux ajoutez un groupe dynamique 4 et devez-le inclure le groupe 3:

4,3

Assemblage ensemble de ce que j'ai appris de MySQL CURSOR 'S, j'ai créé la procédure stockée suivante appelée fetch_inheritance_groups:

DROP PROCEDURE IF EXISTS `fetch_inheritance_groups`;

CREATE PROCEDURE `fetch_inheritance_groups`(IN parent_id INT)
    READS SQL DATA
    BEGIN

    DECLARE inherit_id char(32);
    DECLARE eol BOOLEAN;
    DECLARE inherit_cur CURSOR FOR SELECT r.Related_Group_ID FROM usr_relationships r WHERE r.Group_ID = parent_id AND r.Related_Group_ID IS NOT NULL;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET eol = TRUE;

    OPEN inherit_cur;
    inherited: LOOP
        FETCH inherit_cur INTO inherit_id;

        IF eol 
            THEN
            CLOSE inherit_cur;
            LEAVE inherited;
        ELSE
            CALL fetch_inheritance_groups(inherit_id);
        END IF;

        SELECT inherit_id;

    END LOOP inherited;

END;

... ce qui s'appelle avec ...

CALL fetch_inheritance_groups(4);

... et retourne les résultats attendus de

3
2
1

Dans ce cas, cela commence mon problème, ils sont retournés comme trois ensembles de résultats distincts plutôt que trois lignes dans un ensemble de résultats unique, et deuxièmement, les résultats sont destinés à être utilisés dans une requête comme suit:

SELECT r.uer_id FROM relationships r WHERE r.group_id = 4 OR r.group_id IN (CALL fetch_inheritance_groups(4));

Donc, les trois questions que j'espère que quelqu'un puisse répondre sont:

1. Dois-je utiliser un CURSOR ici ou y a-t-il une alternative qui obtiendra le même résultat récursif?

2. Comment récupérer les résultats dans un ensemble de résultats unique afin qu'il puisse être utilisé de la même manière qu'un sous-cote?

3. Quelle est la manière appropriée d'utiliser les résultats du CALL parce que je ne peux pas au moins aussi loin que j'ai essayé d'obtenir l'exemple de l'énoncé de sélection ci-dessus pour fonctionner? Je crois que c'est parce que je ne peux pas utiliser CALL en ligne mais je ne suis pas sûr.


Mise à jour: Dans une question connexe ici: Combien de temps une table de mémoire temporaire persiste si je ne le dépose pas (mysql) = J'ai posté un ensemble de mises à jour de la procédure stockée utilisée ci-dessus qui est toujours récursive, mais fournit la liste des identifiants en tant que variable et dans un format compatible avec Find_in_set () me donnant exactement ce dont j'avais besoin.

2
oucil

Donc, les trois questions que j'espère que quelqu'un puisse répondre sont:

1. Je voudrais utiliser un curseur ici ou y a-t-il une alternative qui obtiendra le même résultat récursif?

Non tu ne devrais pas. Le curseur reste ouvert sur et finir. Penser aux frais généraux me faire grincer. Personnellement, je reste loin des curseurs.

2.Comment puis-je obtenir les résultats dans un ensemble de résultats unique afin qu'il puisse être utilisé de la même manière qu'un sous-domaine?

3.Quelle est la bonne façon d'utiliser les résultats de l'appel parce que je ne peux pas au moins aussi loin que j'ai essayé d'obtenir l'exemple de l'énoncé de sélection ci-dessus pour fonctionner? Je crois que c'est parce que je ne peux pas utiliser l'appel en ligne, mais je ne suis pas sûr.

J'aimerais vous faire sauter de cela en suggérant quelque chose que j'ai appris de mes jours d'université lors de l'apprentissage des structures de données. Vous devez effectuer une traversée d'arbres à l'aide d'une file d'attente. Cela vous permet de commencer à une racine et d'exprimer tous les descendants d'un arbre.

L'algorithme va comme ça

  • Étape 01: Commencez par une file d'attente vide
  • Étape 02: DEQUEUE Node de l'avant de la file d'attente
  • Étape 03: Enqueue Tous les enfants du dernier noeud
  • Étape 04: Info de processus du dernier noeud
  • Étape 05: Si la file d'attente n'est pas vide, retournez à l'étape 02
  • Étape 06: Tout fait

Cela vous permet de traverser une structure récursive sans utiliser de récursivité programmatique. À ce stade, vous demandez probablement: comment puis-je parcourir une structure d'arbres sans récursion?

J'ai écrit un message sur la façon de scripter trois procédures stockées qui peuvent utiliser une boucle dans un seul appel qui traversera une table avec des nœuds et son parent dans une table:

  • GetParenIDByID
  • Gaine
  • GetFamilytree

Le post est Trouvez le niveau le plus élevé d'un champ hiérarchique: avec VS sans CTES (24 oct. 2011). Il contient les procédures stockées déjà écrites qui traverseront la structure de la table suivante:

+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment | 
| parent_id  | int(11)      | YES  |     | NULL    |                | 
| name       | varchar(255) | YES  |     | NULL    |                | 
| notes      | text         | YES  |     | NULL    |                | 
+------------+--------------+------+-----+---------+----------------+

Veuillez lire attentivement le code et appliquer les principes.

Essaie !!!

3
RolandoMySQLDBA