MySQL: query gerarchica ad albero


20

SUB-TREE ALL'INTERNO DI UN ALBERO in MySQL

Nel mio MYSQL Database COMPANY, ho Table: Employeeun'associazione ricorsiva, un dipendente può essere capo di un altro dipendente. A self relationship of kind (SuperVisor (1)- SuperVisee (∞) ).

Query per creare una tabella:

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;

Ho inserito una serie di tuple (query):

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")  

Le righe inserite hanno la seguente relazione gerarchica-albero :

         A     <---ROOT-OWNER
        /|\             
       / A \        
      B     F 
    //| \    \          
   // |  \    K     
  / | |   \                     
 I  L H    C        
/     |   / \ 
J     G  D   E

Ho scritto una query per trovare una relazione:

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;

E l'output è:

+------------+------------+----------+
| 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)

[ DOMANDA ]
Invece di completare l'albero gerarchico, ho bisogno SUB-TREEdi un punto (selettivo), ad esempio:
se l'argomento di input è, l' Boutput dovrebbe essere come sotto ...

+------------+------------+----------+
| SuperVisor | SuperVisee | COUNT(*) |
+------------+------------+----------+
| B          | C,H,I,L    |        4 |
| C          | D,E        |        2 |
| H          | G          |        1 |
| I          | J          |        1 |
+------------+------------+----------+   

Per favore, aiutatemi su questo. In caso contrario, una procedura memorizzata può essere utile.
Ci ho provato, ma tutti gli sforzi erano inutili!



Ho semplicemente fornito un framework di test per la community da utilizzare nell'esplorazione di questa domanda più facilmente.
mellamokb,

@bluefeet Sì, una volta ricevuta la risposta rimuoverò una di queste due.
Grijesh Chauhan,

1
@GrijeshChauhan, lascia che ti chieda questo: qual è meglio creare le tue onde visibili? Gettare ciottoli nell'oceano o lanciare pietre in un piccolo stagno? Andare direttamente dagli esperti ti darà quasi sicuramente la risposta migliore, e questo tipo di domanda è così importante (argomenti di database avanzati) che gli abbiamo dato il suo sito sulla rete. Ma non ti impedirò di chiederti dove vuoi, questa è la tua prerogativa. La mia prerogativa è quella di votare per spostarlo su un altro sito se penso che sia quello a cui appartiene. : D Usiamo entrambi la rete come riteniamo opportuno in questo caso: D
jcolebrand

1
@jcolebrand: in realtà è stata solo colpa mia. Uso le domande su più lati per ottenere una risposta migliore, rapida e molte. It my experience Ho sempre avuto una risposta migliore da parte di esperti . E penso che sia stata una decisione migliore spostare la domanda agli amministratori del database. In tutti i casi, sono molto grato a StackOverflow e alle persone che sono attive qui. Ho davvero trovato una soluzione per molti problemi che è stato molto difficile trovare me stesso o qualsiasi altra rete.
Grijesh Chauhan,

Risposte:


5

Ho già affrontato qualcosa di questo tipo usando Stored Procedures: Trova il livello più alto di un campo gerarchico: con vs senza CTE (24 ottobre 2011)

Se guardi nel mio post, puoi usare le funzioni GetAncestry e GetFamilyTree come modello per attraversare l'albero da un determinato punto.

AGGIORNAMENTO 2012-12-11 12:11 EDT

Ho guardato indietro al mio codice dal mio post . Ho scritto la funzione memorizzata per te:

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 $$

Funziona davvero. Ecco un esempio:

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>

C'è solo un problema. Ho aggiunto una riga in più per il proprietario

  • Il proprietario ha SSN 0
  • Il proprietario è il suo capo con MSSN 0

Ecco i dati

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>

capito l'idea!
Grijesh Chauhan,

Come posso adattarmi per ottenere tutti i Discendenti in Aquesto modo A A/B A/B/C A/B/C/D A/B/C/E A/B/H A/B/H/G A/B/I A/B/I/J A/B/L A/F A/F/K
Smith,

gestisce anche i multinodi? come è appeso nel mio database in cui sono stati trovati più nodi di un genitore
عثمان غني

3

Quello che stai usando si chiama modello di elenco di adiacenza . Ha molte limitazioni. Si verificherà un problema quando si desidera eliminare / inserire un nodo in un luogo specifico. È meglio usare il modello di set nidificato .

C'è una spiegazione dettagliata . Sfortunatamente l'articolo su mysql.com non esiste più.


5
" ha molte limitazioni " - ma solo quando si utilizza MySQL. Quasi tutti i DBMS supportano query ricorsive (MySQL è una delle poche che non lo fanno) e questo rende il modello davvero facile da gestire.
a_horse_with_no_name

@a_horse_with_no_name Non ho mai usato altro che MySQL. Quindi non l'ho mai saputo. Grazie per l'informazione.
Shiplu Mokaddim,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.