web-dev-qa-db-fra.com

Se connecter par équivalent antérieur pour MySQL

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

18
Jake

MySQL ne supporte pas les requêtes récursives, vous devez donc le faire à la dure:

  1. Sélectionnez les lignes où ParentID = XX est votre racine.
  2. Recueillez les valeurs Id de (1).
  3. Répétez (1) pour chaque Id de (2).
  4. Continuez à récurer à la main jusqu'à ce que vous trouviez tous les nœuds feuilles.

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 .

10
mu is too short

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/

2
Yashpal Singla

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 ;
2
Mats Kindahl

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
1
user5132533

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;
0
Stan Sokolov