sql >> データベース >  >> RDS >> Mysql

MySQLの階層データから深度ベースのツリーを生成する(CTEなし)

    ストアドプロシージャを使用する場合は、phpからmysqlへの1回の呼び出しでそれを行うことができます:

    呼び出し例

    mysql> call category_hier(1);
    
    +--------+---------------+---------------+----------------------+-------+
    | cat_id | category_name | parent_cat_id | parent_category_name | depth |
    +--------+---------------+---------------+----------------------+-------+
    |      1 | Location      |          NULL | NULL                 |     0 |
    |      3 | USA           |             1 | Location             |     1 |
    |      4 | Illinois      |             3 | USA                  |     2 |
    |      5 | Chicago       |             3 | USA                  |     2 |
    +--------+---------------+---------------+----------------------+-------+
    4 rows in set (0.00 sec)
    
    
    $sql = sprintf("call category_hier(%d)", $id);
    

    これがお役に立てば幸いです:)

    完全なスクリプト

    テストテーブルの構造:

    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),
       ('USA',1), 
          ('Illinois',2), 
          ('Chicago',2),  
    ('Color',null), 
       ('Black',3), 
       ('Red',3);
    

    手順:

    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;
    
    /* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
    
    create temporary table tmp engine=memory select * from hier;
    
    while not v_done do
    
        if exists( select 1 from categories p inner join hier on p.parent_cat_id = hier.cat_id and hier.depth = v_depth) then
    
            insert into hier 
                select p.parent_cat_id, p.cat_id, v_depth + 1 from categories p 
                inner join tmp on p.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 
     p.cat_id,
     p.name as category_name,
     b.cat_id as parent_cat_id,
     b.name as parent_category_name,
     hier.depth
    from 
     hier
    inner join categories p on hier.cat_id = p.cat_id
    left outer join categories b on hier.parent_cat_id = b.cat_id
    order by
     hier.depth, hier.cat_id;
    
    drop temporary table if exists hier;
    drop temporary table if exists tmp;
    
    end #
    

    テスト実行:

    delimiter ;
    
    call category_hier(1);
    
    call category_hier(2);
    

    Yahooジオプラネットプレースデータを使用したいくつかのパフォーマンステスト

    drop table if exists geoplanet_places;
    create table geoplanet_places
    (
    woe_id int unsigned not null,
    iso_code  varchar(3) not null,
    name varchar(255) not null,
    lang varchar(8) not null,
    place_type varchar(32) not null,
    parent_woe_id int unsigned not null,
    primary key (woe_id),
    key (parent_woe_id)
    )
    engine=innodb;
    
    mysql> select count(*) from geoplanet_places;
    +----------+
    | count(*) |
    +----------+
    |  5653967 |
    +----------+
    

    これで、テーブルの560万行(場所)になります。phpから呼び出された隣接リストの実装/ストアドプロシージャがそれをどのように処理するかを見てみましょう。

         1 records fetched with max depth 0 in 0.001921 secs
       250 records fetched with max depth 1 in 0.004883 secs
       515 records fetched with max depth 1 in 0.006552 secs
       822 records fetched with max depth 1 in 0.009568 secs
       918 records fetched with max depth 1 in 0.009689 secs
      1346 records fetched with max depth 1 in 0.040453 secs
      5901 records fetched with max depth 2 in 0.219246 secs
      6817 records fetched with max depth 1 in 0.152841 secs
      8621 records fetched with max depth 3 in 0.096665 secs
     18098 records fetched with max depth 3 in 0.580223 secs
    238007 records fetched with max depth 4 in 2.003213 secs
    

    全体として、これらのコールドランタイムにはかなり満足しています。何万行ものデータをフロントエンドに返すことを検討し始めるのではなく、呼び出しごとに数レベルのみを動的にフェッチするツリーを構築したいからです。ああ、念のために言っておきますが、innodbはmyisamよりも遅いと思っていました。私がテストしたmyisamの実装は、すべての点で2倍遅くなりました。

    その他の情報: http://pastie.org/1672733

    これがお役に立てば幸いです:)



    1. SQLiteサブクエリ

    2. SQLiteデータベースのテーブルを一覧表示する2つの方法

    3. PostgreSQLクエリの実行時間を取得する

    4. MariaDBSkySQLを使用してクラウドでより高く上昇