Count all child nodes of hierarchical data in a table

前端 未结 3 1288
失恋的感觉
失恋的感觉 2020-12-06 14:02

I want to count number of all child nodes under any level of tree structure maintained in a table using adjacency model (parent-child key). Table structure and data looks l

3条回答
  •  春和景丽
    2020-12-06 14:40

    Can be done fairly simply with a non-recursive stored procedure as follows:

    Example calls

    mysql> call category_hier(1);
    +--------------+
    | num_children |
    +--------------+
    |            3 |
    +--------------+
    1 row in set (0.00 sec)
    
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> call category_hier(2);
    +--------------+
    | num_children |
    +--------------+
    |            2 |
    +--------------+
    1 row in set (0.00 sec)
    
    Query OK, 0 rows affected (0.00 sec)
    

    Full script

    drop table if exists categories;
    create table categories
    (
    cat_id smallint unsigned not null auto_increment primary key,
    name varchar(255) not null,
    parent_cat_id smallint unsigned null,
    key (parent_cat_id)
    )
    engine = innodb;
    
    insert into categories (name, parent_cat_id) values
    ('Location',null), 
    ('Color',null), 
       ('USA',1), 
          ('Illinois',3), 
          ('Chicago',3), 
       ('Black',2), 
       ('Red',2);
    
    
    drop procedure if exists category_hier;
    delimiter #
    
    create procedure category_hier
    (
    in p_cat_id smallint unsigned
    )
    begin
    
    declare v_done tinyint unsigned default 0;
    declare v_depth smallint unsigned default 0;
    
    create temporary table hier(
     parent_cat_id smallint unsigned, 
     cat_id smallint unsigned, 
     depth smallint unsigned default 0
    )engine = memory;
    
    insert into hier select parent_cat_id, cat_id, v_depth from categories where cat_id = p_cat_id;
    create temporary table tmp engine=memory select * from hier;
    
    /* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
    
    while not v_done do
    
        if exists( select 1 from categories c
            inner join tmp on c.parent_cat_id = tmp.cat_id and tmp.depth = v_depth) then
    
            insert into hier select c.parent_cat_id, c.cat_id, v_depth + 1 from categories c
                inner join tmp on c.parent_cat_id = tmp.cat_id and tmp.depth = v_depth;
    
            set v_depth = v_depth + 1;          
    
            truncate table tmp;
            insert into tmp select * from hier where depth = v_depth;
    
        else
            set v_done = 1;
        end if;
    
    end while;
    
    /*
    select 
     c.cat_id,
     c.name as category_name,
     p.cat_id as parent_cat_id,
     p.name as parent_category_name,
     hier.depth
    from 
     hier
    inner join categories c on hier.cat_id = c.cat_id
    left outer join categories p on hier.parent_cat_id = p.cat_id
    order by
     hier.depth;
    */
    
    select count(*) as num_children from hier where parent_cat_id is not null;
    
    drop temporary table if exists hier;
    drop temporary table if exists tmp;
    
    end #
    
    delimiter ;
    
    call category_hier(1);
    
    call category_hier(2);
    

    You can easily adapt this example to suit your requirements.

    Hope it helps :)

提交回复
热议问题