AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 30021
Accepted
Grijesh Chauhan
Grijesh Chauhan
Asked: 2012-12-07 07:36:41 +0800 CST2012-12-07 07:36:41 +0800 CST 2012-12-07 07:36:41 +0800 CST

MySQL: consulta hierárquica em árvore

  • 772

SUB-ÁRVORE DENTRO DE UMA ÁRVORE no MySQL

No meu MYSQL Database COMPANY, tenho uma Table: Employeeassociação recursiva, um funcionário pode ser chefe de outro funcionário. A self relationship of kind (SuperVisor (1)- SuperVisee (∞) ).

Consulta para criar tabela:

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;

Eu inseri um conjunto de tuplas (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")  

As linhas inseridas têm o seguinte Tree-Hierarchical-Relationship :

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

Eu escrevi uma consulta para encontrar o relacionamento:

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 a saída é:

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

[ PERGUNTA ]
Em vez de uma árvore hierárquica completa, preciso SUB-TREEde um ponto (seletivo), por exemplo:
Se Bo argumento de entrada for, a saída deve ser como abaixo ...

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

Por favor me ajude nisso. Se não for uma consulta, um procedimento armazenado pode ser útil.
Eu tentei, mas todos os esforços foram inúteis!

mysql stored-procedures
  • 2 2 respostas
  • 68709 Views

2 respostas

  • Voted
  1. Best Answer
    RolandoMySQLDBA
    2012-12-11T12:09:38+08:002012-12-11T12:09:38+08:00

    Eu já abordei algo dessa natureza usando Stored Procedures : Encontre o nível mais alto de um campo hierárquico: com vs sem CTEs (24 de outubro de 2011)

    Se você olhar no meu post, você pode usar as funções GetAncestry e GetFamilyTree como um modelo para percorrer a árvore a partir de qualquer ponto.

    ATUALIZAÇÃO 2012-12-11 12:11 EDT

    Eu olhei para o meu código do meu post . Eu escrevi a função armazenada para você:

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

    Ele realmente funciona. Aqui está uma amostra:

    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>
    

    Há apenas uma captura. Eu adicionei uma linha extra para o proprietário

    • O proprietário tem SSN 0
    • O dono é seu próprio patrão com MSSN 0

    Aqui estão os dados

    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>
    
    • 5
  2. Shiplu Mokaddim
    2012-12-07T07:46:18+08:002012-12-07T07:46:18+08:00

    O que você está usando é chamado de Modelo de Lista de Adjacência . Tem muitas limitações. Você terá problemas quando quiser excluir/inserir um nó em um local específico. É melhor você usar o modelo de conjunto aninhado .

    Há uma explicação detalhada . Infelizmente o artigo no mysql.com não existe mais.

    • 3

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como ver a lista de bancos de dados no Oracle?

    • 8 respostas
  • Marko Smith

    Quão grande deve ser o mysql innodb_buffer_pool_size?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    restaurar a tabela do arquivo .frm e .ibd?

    • 10 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Como selecionar a primeira linha de cada grupo?

    • 6 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve