Tout,
J'ai trois champs dans une table qui définissent une relation parent-enfant présente dans une base de données MySQL version 5.0. Le nom de la table est tb_Tree et contient les données suivantes:
Table Name: tb_Tree
Id | ParentId | Name
--------------------
1 | 0 | Fruits
2 | 0 | Vegetables
3 | 1 | Apple
4 | 1 | Orange
5 | 2 | Cabbage
6 | 2 | Eggplant
Comment puis-je écrire une requête pour obtenir tous les enfants si un ParentId est spécifié. Notez que les entrées de table données ne sont que des exemples de données et qu'elles peuvent avoir beaucoup plus de lignes. Oracle a une clause "CONNECT BY PRIOR", mais je n'ai rien trouvé de similaire pour MySQL. Quelqu'un peut-il s'il vous plaît conseiller?
Merci
MySQL ne supporte pas les requêtes récursives, vous devez donc le faire à la dure:
ParentID = X
où X
est votre racine.Id
de (1).Id
de (2).Si vous connaissez une profondeur maximale, vous pouvez joindre votre table à elle-même (à l'aide de LEFT OUTER JOINs) jusqu'à la profondeur maximale possible, puis nettoyer les valeurs NULL.
Vous pouvez également changer votre représentation en arbre en jeux imbriqués .
Vous pouvez également consulter ce blog intéressant, qui montre comment obtenir des résultats similaires dans mysql
http://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/
C'est un vieux fil, mais depuis que j'ai eu la question dans un autre forum, j'ai pensé l'ajouter ici. Dans ce cas, j'ai créé une procédure stockée codée en dur pour gérer le cas spécifique. Ceci a bien sûr quelques inconvénients car tous les utilisateurs ne peuvent pas créer des procédures stockées à volonté, mais néanmoins.
Considérez le tableau suivant avec les nœuds et les enfants:
CREATE TABLE nodes (
parent INT,
child INT
);
INSERT INTO nodes VALUES
( 5, 2), ( 5, 3),
(18, 11), (18, 7),
(17, 9), (17, 8),
(26, 13), (26, 1), (26,12),
(15, 10), (15, 5),
(38, 15), (38, 17), (38, 6),
(NULL, 38), (NULL, 26), (NULL, 18);
Avec cette table, la procédure stockée suivante calcule un ensemble de résultats composé de tous les descendants du noeud fourni:
delimiter $$
CREATE PROCEDURE find_parts(seed INT)
BEGIN
-- Temporary storage
DROP TABLE IF EXISTS _result;
CREATE TEMPORARY TABLE _result (node INT PRIMARY KEY);
-- Seeding
INSERT INTO _result VALUES (seed);
-- Iteration
DROP TABLE IF EXISTS _tmp;
CREATE TEMPORARY TABLE _tmp LIKE _result;
REPEAT
TRUNCATE TABLE _tmp;
INSERT INTO _tmp SELECT child AS node
FROM _result JOIN nodes ON node = parent;
INSERT IGNORE INTO _result SELECT node FROM _tmp;
UNTIL ROW_COUNT() = 0
END REPEAT;
DROP TABLE _tmp;
SELECT * FROM _result;
END $$
delimiter ;
La liste ci-dessous select
répertorie toutes les plantes et leur parentid
jusqu’à 4 niveaux (et vous pouvez bien entendu étendre le niveau):
select id, name, parentid
,(select parentid from tb_tree where id=t.parentid) parentid2
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4
from tb_tree t
et vous pouvez ensuite utiliser cette requête pour obtenir le résultat final. Par exemple, vous pouvez obtenir tous les enfants de "Fruits" par le code ci-dessous:
select id ,name from (
select id, name, parentid
,(select parentid from tb_tree where id=t.parentid) parentid2
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4
from tb_tree t) tt
where ifnull(parentid4,0)=1 or ifnull(parentid3,0)=1 or ifnull(parentid2,0)=1 or ifnull(parentid,0)=1
La procédure stockée ci-dessous commande une table comportant des lignes avec une référence en arrière à la précédente. Avis sur la première étape, je copie des lignes dans la table temporaire - ces lignes correspondent à une condition. Dans mon cas, ce sont des lignes qui appartiennent à la même linéaire (route utilisée dans la navigation GPS). Le domaine d'activité n'est pas important. Juste dans mon cas, je trie des segments qui appartiennent à la même route
DROP PROCEDURE IN EXISTS orderLocations; DELIMITER //
CREATE PROCEDURE orderLocations (_full_linear_code VARCHAR (11)) BEGIN
DECLARE _code VARCHAR(11);
DECLARE _id INT(4);
DECLARE _count INT(4);
DECLARE _pos INT(4);
DROP TEMPORARY TABLE IF EXISTS temp_sort;
CREATE TEMPORARY TABLE temp_sort (
id INT(4) PRIMARY KEY,
pos INT(4),
code VARCHAR(11),
prev_code VARCHAR(11)
);
-- copy all records to sort into temp table - this way sorting would go all in memory
INSERT INTO temp_sort SELECT
id, -- this is primary key of original table
NULL, -- this is position that still to be calculated
full_tmc_code, -- this is a column that references sorted by
negative_offset -- this is a reference to the previous record (will be blank for the first)
FROM tmc_file_location
WHERE linear_full_tmc_code = _full_linear_code;
-- this is how many records we have to sort / update position
SELECT count(*)
FROM temp_sort
INTO _count;
-- first position index
SET _pos = 1;
-- pick first record that has no prior record
SELECT
code,
id
FROM temp_sort l
WHERE prev_code IS NULL
INTO _code, _id;
-- update position of the first record
UPDATE temp_sort
SET pos = _pos
WHERE id = _id;
-- all other go by chain link
WHILE (_pos < _count) DO
SET _pos = _pos +1;
SELECT
code,
id
FROM temp_sort
WHERE prev_code = _code
INTO _code, _id;
UPDATE temp_sort
SET pos = _pos
WHERE id = _id;
END WHILE;
-- join two tables and return position along with all other fields
SELECT
t.pos,
l.*
FROM tmc_file_location l, temp_sort t
WHERE t.id = l.id
ORDER BY t.pos;
END;