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

入れ子集合を使用して動的メニューを作成する

    次のクエリを使用すると、SQLのhave句とMySQLの group_concat 機能。

    以下は、私が使用したテーブル定義とサンプルデータです。

    drop table nested_set;
    
    CREATE TABLE nested_set (
     id INT,
     name VARCHAR(20) NOT NULL,
     lft INT NOT NULL,
     rgt INT NOT NULL
    );
    
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (1,'HEAD',1,28);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (2,'A',2,3);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (3,'B',4,17);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (4,'B1',5,10);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (5,'B1.1',6,7);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (6,'B1.2',8,9);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (7,'B2',11,16);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (8,'B2.1',12,13);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (9,'B2.2',14,15);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (10,'C',18,25);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (11,'C1',19,20);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (12,'C2',21,22);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (13,'C3',23,24);
    INSERT INTO nested_set (id, name, lft, rgt) VALUES (14,'D',26,27);
    

    次のクエリは、ツリー全体(HEADを除く)を提供します。

    SELECT
      node.id
    , node.lft
    , node.rgt
    , node.name
    ,  GROUP_CONCAT(parent.name ORDER BY parent.lft  SEPARATOR "/" ) AS path
    ,  (COUNT(parent.lft) - 1) AS depth
    FROM nested_set AS node
    inner join nested_set AS parent
    on node.lft BETWEEN parent.lft AND parent.rgt
    where parent.lft > 1
    GROUP BY node.id
    

    サンプルデータに対して実行すると、次の出力が表示されます。

    +------+-----+-----+------+-----------+-------+
    | id   | lft | rgt | name | path      | depth |
    +------+-----+-----+------+-----------+-------+
    |    2 |   2 |   3 | A    | A         |     0 |
    |    3 |   4 |  17 | B    | B         |     0 |
    |    4 |   5 |  10 | B1   | B/B1      |     1 |
    |    5 |   6 |   7 | B1.1 | B/B1/B1.1 |     2 |
    |    6 |   8 |   9 | B1.2 | B/B1/B1.2 |     2 |
    |    7 |  11 |  16 | B2   | B/B2      |     1 |
    |    8 |  12 |  13 | B2.1 | B/B2/B2.1 |     2 |
    |    9 |  14 |  15 | B2.2 | B/B2/B2.2 |     2 |
    |   10 |  18 |  25 | C    | C         |     0 |
    |   11 |  19 |  20 | C1   | C/C1      |     1 |
    |   12 |  21 |  22 | C2   | C/C2      |     1 |
    |   13 |  23 |  24 | C3   | C/C3      |     1 |
    |   14 |  26 |  27 | D    | D         |     0 |
    +------+-----+-----+------+-----------+-------+
    

    上記のクエリに次のように追加すると、さまざまなセクションを開くために必要なコントロールが得られます。

    having
    depth = 0
    or ('<PATH_TO_OPEN>' =  left(path, length('<PATH_TO_OPEN>'))
       and depth = length('<PATH_TO_OPEN>') - length(replace('<PATH_TO_OPEN>', '/', '')) + 1)
    

    have句は、groupbyクエリの結果にフィルターを適用します。 「depth=0」の部分は、常にベースメニューノード(A、B、C、およびD)がないことを確認するためのものです。次の部分は、開いているノードを制御する部分です。ノードのパスを、開きたい設定パス('')と比較して、一致するかどうかを確認します。また、パスに対してレベルを開くだけであることを確認します。 ''ロジックを含むセクション全体またはセクションを複製し、必要に応じて追加して、必要に応じて複数のパスを開くことができます。 ''が末尾のスラッシュ(/)で終わっていないことを確認してください。

    以下は、必要な出力を取得するためのクエリを作成する方法を示すいくつかの出力例です。

    =========Open B==========
    
    SELECT
      node.id
    , node.lft
    , node.rgt
    , node.name
    ,  GROUP_CONCAT(parent.name ORDER BY parent.lft  SEPARATOR "/" ) AS path
    ,  (COUNT(parent.lft) - 1) AS depth
    FROM nested_set AS node
    inner join nested_set AS parent
    on node.lft BETWEEN parent.lft AND parent.rgt
    where parent.lft > 1
    GROUP BY node.id
    having
    depth = 0
    or ('B' =  left(path, length('B'))
       and depth = length('B') - length(replace('B', '/', '')) + 1)
    
    +------+-----+-----+------+------+-------+
    | id   | lft | rgt | name | path | depth |
    +------+-----+-----+------+------+-------+
    |    2 |   2 |   3 | A    | A    |     0 |
    |    3 |   4 |  17 | B    | B    |     0 |
    |    4 |   5 |  10 | B1   | B/B1 |     1 |
    |    7 |  11 |  16 | B2   | B/B2 |     1 |
    |   10 |  18 |  25 | C    | C    |     0 |
    |   14 |  26 |  27 | D    | D    |     0 |
    +------+-----+-----+------+------+-------+
    
    =========Open B and B/B1==========
    
    SELECT
      node.id
    , node.lft
    , node.rgt
    , node.name
    ,  GROUP_CONCAT(parent.name ORDER BY parent.lft  SEPARATOR "/" ) AS path
    ,  (COUNT(parent.lft) - 1) AS depth
    FROM nested_set AS node
    inner join nested_set AS parent
    on node.lft BETWEEN parent.lft AND parent.rgt
    where parent.lft > 1
    GROUP BY node.id
    having
    depth = 0
    or ('B' =  left(path, length('B'))
       and depth = length('B') - length(replace('B', '/', '')) + 1)
    or ('B/B1' =  left(path, length('B/B1'))
       and depth = length('B/B1') - length(replace('B/B1', '/', '')) + 1)
    
    +------+-----+-----+------+-----------+-------+
    | id   | lft | rgt | name | path      | depth |
    +------+-----+-----+------+-----------+-------+
    |    2 |   2 |   3 | A    | A         |     0 |
    |    3 |   4 |  17 | B    | B         |     0 |
    |    4 |   5 |  10 | B1   | B/B1      |     1 |
    |    5 |   6 |   7 | B1.1 | B/B1/B1.1 |     2 |
    |    6 |   8 |   9 | B1.2 | B/B1/B1.2 |     2 |
    |    7 |  11 |  16 | B2   | B/B2      |     1 |
    |   10 |  18 |  25 | C    | C         |     0 |
    |   14 |  26 |  27 | D    | D         |     0 |
    +------+-----+-----+------+-----------+-------+
    
    =========Open B and B/B1 and C==========
    
    SELECT
      node.id
    , node.lft
    , node.rgt
    , node.name
    ,  GROUP_CONCAT(parent.name ORDER BY parent.lft  SEPARATOR "/" ) AS path
    ,  (COUNT(parent.lft) - 1) AS depth
    FROM nested_set AS node
    inner join nested_set AS parent
    on node.lft BETWEEN parent.lft AND parent.rgt
    where parent.lft > 1
    GROUP BY node.id
    having
    depth = 0
    or ('B' =  left(path, length('B'))
       and depth = length('B') - length(replace('B', '/', '')) + 1)
    or ('B/B1' =  left(path, length('B/B1'))
       and depth = length('B/B1') - length(replace('B/B1', '/', '')) + 1)
    or ('C' =  left(path, length('C'))
       and depth = length('C') - length(replace('C', '/', '')) + 1)
    
    +------+-----+-----+------+-----------+-------+
    | id   | lft | rgt | name | path      | depth |
    +------+-----+-----+------+-----------+-------+
    |    2 |   2 |   3 | A    | A         |     0 |
    |    3 |   4 |  17 | B    | B         |     0 |
    |    4 |   5 |  10 | B1   | B/B1      |     1 |
    |    5 |   6 |   7 | B1.1 | B/B1/B1.1 |     2 |
    |    6 |   8 |   9 | B1.2 | B/B1/B1.2 |     2 |
    |    7 |  11 |  16 | B2   | B/B2      |     1 |
    |   10 |  18 |  25 | C    | C         |     0 |
    |   11 |  19 |  20 | C1   | C/C1      |     1 |
    |   12 |  21 |  22 | C2   | C/C2      |     1 |
    |   13 |  23 |  24 | C3   | C/C3      |     1 |
    |   14 |  26 |  27 | D    | D         |     0 |
    +------+-----+-----+------+-----------+-------+
    

    それについてです。開く必要のあるパスごとに、そのセクションまたはセクションを複製し続けるだけです。

    http://mikehillyer.com/articles/managing-hierarchical-dataを参照してください-in-mysql / MySQLでのネストされたセットの操作に関する一般的な情報が必要な場合に備えて。

    ご不明な点がございましたら、お気軽にお問い合わせください。

    HTH、

    -Dipin



    1. 修正方法「WITHRESULTSETS句で結果セットに2列が指定されたため、EXECUTEステートメントが失敗しました…」SQLServerのメッセージ11537

    2. MySQLタイムスタンプ選択の日付範囲

    3. 保存されたmd5文字列をMySQLで10進値に変換します

    4. PHPを使用してmysqlデータベースに写真を追加する方法は?