Dans mon MYSQL Database COMPANY
, J'ai un Table: Employee
Avec association récursive, un employé peut être le patron d'un autre employé. A self relationship of kind (SuperVisor (1)- SuperVisee (∞) )
.
Requête pour créer une table:
CREATE TABLE IF NOT EXISTS `Employee` (
`SSN` varchar(64) NOT NULL,
`Name` varchar(64) DEFAULT NULL,
`Designation` varchar(128) NOT NULL,
`MSSN` varchar(64) NOT NULL,
PRIMARY KEY (`SSN`),
CONSTRAINT `FK_Manager_Employee`
FOREIGN KEY (`MSSN`) REFERENCES Employee(SSN)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
J'ai inséré un ensemble de tuples (Requête):
INSERT INTO Employee VALUES
("1", "A", "OWNER", "1"),
("2", "B", "BOSS", "1"), # Employees under OWNER
("3", "F", "BOSS", "1"),
("4", "C", "BOSS", "2"), # Employees under B
("5", "H", "BOSS", "2"),
("6", "L", "WORKER", "2"),
("7", "I", "BOSS", "2"),
# Remaining Leaf nodes
("8", "K", "WORKER", "3"), # Employee under F
("9", "J", "WORKER", "7"), # Employee under I
("10","G", "WORKER", "5"), # Employee under H
("11","D", "WORKER", "4"), # Employee under C
("12","E", "WORKER", "4")
Les lignes insérées ont ce qui suit Tree-Hierarchical-Relationship:
A <---ROOT-OWNER
/|\
/ A \
B F
//| \ \
// | \ K
/ | | \
I L H C
/ | / \
J G D E
J'ai écrit une requête pour trouver une relation:
SELECT SUPERVISOR.name AS SuperVisor,
GROUP_CONCAT(SUPERVISEE.name ORDER BY SUPERVISEE.name ) AS SuperVisee,
COUNT(*)
FROM Employee AS SUPERVISOR
INNER JOIN Employee SUPERVISEE ON SUPERVISOR.SSN = SUPERVISEE.MSSN
GROUP BY SuperVisor;
Et la sortie est:
+------------+------------+----------+
| SuperVisor | SuperVisee | COUNT(*) |
+------------+------------+----------+
| A | A,B,F | 3 |
| B | C,H,I,L | 4 |
| C | D,E | 2 |
| F | K | 1 |
| H | G | 1 |
| I | J | 1 |
+------------+------------+----------+
6 rows in set (0.00 sec)
[ QUESTION ]
Au lieu d'un arbre hiérarchique complet, j'ai besoin d'un SUB-TREE
À partir d'un point (sélectif), par exemple:
Si l'argument d'entrée est B
alors la sortie doit être comme ci-dessous ...
+------------+------------+----------+
| SuperVisor | SuperVisee | COUNT(*) |
+------------+------------+----------+
| B | C,H,I,L | 4 |
| C | D,E | 2 |
| H | G | 1 |
| I | J | 1 |
+------------+------------+----------+
Veuillez m'aider à ce sujet. Sinon, une procédure stockée peut être utile.
J'ai essayé, mais tous les efforts ont été inutiles!
J'ai déjà abordé quelque chose de cette nature en utilisant des procédures stockées: Trouver le plus haut niveau d'un champ hiérarchique: avec vs sans CTE (24 octobre 2011)
Si vous regardez dans mon article, vous pouvez utiliser les fonctions GetAncestry et GetFamilyTree comme modèle pour parcourir l'arbre à partir d'un point donné.
J'ai regardé en arrière mon code de mon message . J'ai rédigé la fonction stockée pour vous:
DELIMITER $$
DROP FUNCTION IF EXISTS `cte_test`.`GetFamilyTree` $$
CREATE FUNCTION `cte_test`.`GetFamilyTree`(GivenName varchar(64))
RETURNS varchar(1024) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE rv,q,queue,queue_children,queue_names VARCHAR(1024);
DECLARE queue_length,pos INT;
DECLARE GivenSSN,front_ssn VARCHAR(64);
SET rv = '';
SELECT SSN INTO GivenSSN
FROM Employee
WHERE name = GivenName
AND Designation <> 'OWNER';
IF ISNULL(GivenSSN) THEN
RETURN ev;
END IF;
SET queue = GivenSSN;
SET queue_length = 1;
WHILE queue_length > 0 DO
IF queue_length = 1 THEN
SET front_ssn = queue;
SET queue = '';
ELSE
SET pos = LOCATE(',',queue);
SET front_ssn = LEFT(queue,pos - 1);
SET q = SUBSTR(queue,pos + 1);
SET queue = q;
END IF;
SET queue_length = queue_length - 1;
SELECT IFNULL(qc,'') INTO queue_children
FROM
(
SELECT GROUP_CONCAT(SSN) qc FROM Employee
WHERE MSSN = front_ssn AND Designation <> 'OWNER'
) A;
SELECT IFNULL(qc,'') INTO queue_names
FROM
(
SELECT GROUP_CONCAT(name) qc FROM Employee
WHERE MSSN = front_ssn AND Designation <> 'OWNER'
) A;
IF LENGTH(queue_children) = 0 THEN
IF LENGTH(queue) = 0 THEN
SET queue_length = 0;
END IF;
ELSE
IF LENGTH(rv) = 0 THEN
SET rv = queue_names;
ELSE
SET rv = CONCAT(rv,',',queue_names);
END IF;
IF LENGTH(queue) = 0 THEN
SET queue = queue_children;
ELSE
SET queue = CONCAT(queue,',',queue_children);
END IF;
SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1;
END IF;
END WHILE;
RETURN rv;
END $$
Cela fonctionne réellement. Voici un exemple:
mysql> SELECT name,GetFamilyTree(name) FamilyTree
-> FROM Employee WHERE Designation <> 'OWNER';
+------+-----------------------+
| name | FamilyTree |
+------+-----------------------+
| A | B,F,C,H,L,I,K,D,E,G,J |
| G | |
| D | |
| E | |
| B | C,H,L,I,D,E,G,J |
| F | K |
| C | D,E |
| H | G |
| L | |
| I | J |
| K | |
| J | |
+------+-----------------------+
12 rows in set (0.36 sec)
mysql>
Il n'y a qu'une seule prise. J'ai ajouté une ligne supplémentaire pour le propriétaire
Voici les données
mysql> select * from Employee;
+-----+------+-------------+------+
| SSN | Name | Designation | MSSN |
+-----+------+-------------+------+
| 0 | A | OWNER | 0 |
| 1 | A | BOSS | 0 |
| 10 | G | WORKER | 5 |
| 11 | D | WORKER | 4 |
| 12 | E | WORKER | 4 |
| 2 | B | BOSS | 1 |
| 3 | F | BOSS | 1 |
| 4 | C | BOSS | 2 |
| 5 | H | BOSS | 2 |
| 6 | L | WORKER | 2 |
| 7 | I | BOSS | 2 |
| 8 | K | WORKER | 3 |
| 9 | J | WORKER | 7 |
+-----+------+-------------+------+
13 rows in set (0.00 sec)
mysql>
Ce que vous utilisez s'appelle Adjacency List Model . Il a beaucoup de limites. Vous aurez un problème lorsque vous souhaitez supprimer/insérer un nœud à un endroit spécifique. Il vaut mieux que vous utilisiez Nested Set Model .
Il y a explication détaillée . Malheureusement l'article sur mysql.com n'existe plus.