2012-10-04 3 views
4

En supposant que j'ai quelque chose le long des lignes de cet exemple:Nested rejoint sur un arbre naïf mis

CREATE TABLE NaiveTable 
{ 
    id BIGINT NOT NULL, 
    parentId BIGINT NULL, 
    name VARCHAR(20) NULL, 
    CONSTRAINT hierarchy FOREIGN KEY (parentId) REFERENCES NaiveTable(id) 
    PRIMARY KEY (id) 
} 

En tant que parentId note est une référence à id de NaiveTable (dans le cas où j'ai raté la syntaxe exacte).

Les données sont quelque part le long des lignes de ces

+---------+----------+----------+ 
| id | parentId | name | 
+---------+----------+----------+ 
| 1  | null | node1 | 
+---------+----------+----------+ 
| 2  |  1 | node2 | 
+---------+----------+----------+ 
| 3  |  1 | node3 | 
+---------+----------+----------+ 
| 4  |  2 | node4 | 
+---------+----------+----------+ 

Nom de la colonne contient des étiquettes unrealated. Je suis à la recherche d'un moyen de construire une requête SQL sur une table MySQL où toutes les informations seront aplaties et triée hiérarchiquement comme ceci:

node 1, depth 0 
node 2, depth 1 
node 4, depth 2 
node 3, depth 1 

NOTE: Je ne peux pas modifier le schéma de base de données de quelque façon. Je ne peux créer que des requêtes SQL. Aussi, je ne peux pas utiliser le mot-clé WITH puisque MySQL ne le supporte pas. Y at-il un moyen de faire une telle requête? Cependant, toute solution à profondeur deux ou au-delà est considéré assez bon.

EDIT: Voici SQL fiddle si vous aimez expérimenter :)

+0

Le tri provient d'une profondeur en premier de l'arbre? Aussi, pouvez-vous écrire une procédure stockée à la place? Je suppose que ce serait beaucoup plus facile. – jmilloy

+1

Le tri est la profondeur en premier. Oui, des procédures m'ont traversé l'esprit, mais je préférerais une requête SQL. Je voudrais noter que même une requête qui fonctionne pour dire la profondeur de trois serait considérée comme une réponse valide. –

+0

Les réponses à [cette question] (http://stackoverflow.com/questions/7999770/how-to-query-a-mysql-table-to-display-the-root-and-its-subchild) illustrent deux principes possibles: procédures stockées et variables de session – PHeiberg

Répondre

1

Si le schéma de la base de données est fixe et vous ne pouvez pas ajouter/modifier une table, tout ce que vous pouvez faire est de construire l'arbre en mémoire (dans certains langage de programmation), puis essayez de calculer la profondeur de chaque nœud en mémoire. Donc, ma réponse est que vous ne pouvez pas produire votre sortie désirée avec une seule requête!

Mais si vous pouviez modifier le schéma de votre base de données, alors vous voudrez peut-être vérifier this.

+0

Ceci est la solution "correcte". Il est impossible d'ajouter des hiérarchies par la suite sans recourir à un changement de structure. –

0

Voici une petite procédure récursive stockée, qui devrait fonctionner à n'importe quelle profondeur. C'est ma première procédure stockée, alors s'il vous plaît laissez-moi savoir comment l'améliorer.

DROP PROCEDURE IF EXISTS tree_reader; 
DELIMITER $$ 
CREATE PROCEDURE tree_reader(IN parent INT, IN depth INT) READS SQL DATA 
BEGIN 

    DECLARE id_val INT; 
    DECLARE name_val VARCHAR(255); 
    DECLARE _table_name VARCHAR(255); 

    DECLARE no_more_rows BOOLEAN; 
    DECLARE cur CURSOR FOR 
     SELECT id, name 
     FROM tree 
     WHERE IF(parent IS NULL, parentid IS NULL, parentid = parent); 

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE; 

    -- create temporary table on outer call only -- 
    IF depth=0 THEN 
    DROP TABLE IF EXISTS _tree; 
    CREATE TEMPORARY TABLE _tree (id INT, name VARCHAR(255), depth INT); 
    END IF; 

    OPEN cur; 

    -- loop with recursion -- 
    tree_loop: LOOP 
    FETCH cur INTO id_val, name_val; 
    IF no_more_rows THEN LEAVE tree_loop; END IF; 
    INSERT INTO _tree VALUES (id_val, name_val, depth); 
    CALL tree_reader(id_val, depth + 1); 

    END LOOP tree_loop; 

    CLOSE cur; 

    -- output results on outer call only -- 
    IF depth=0 THEN 
    SELECT * FROM _tree; 
    DROP TABLE _tree; 
    END IF; 
END 
$$ 
DELIMITER ; 

Quelques notes:

  • Appel de la procédure: CALL tree_reader(NULL, 0);

  • Vous pouvez utiliser tout id nœud parent, mais le second argument est toujours 0. En pratique, j'ajouterais probablement une procédure wrapper qui ne prend aucun argument et donne l'arbre entier.

  • Vous devez définir la limite de récursivité pour que cela fonctionne: SET max_sp_recursion_depth = 6; (j'ai choisi 6 arbitrairement).