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

MySQLの結果とPHPforD3.jsツリーから階層JSONを作成しますか?

    これは、階層データの適切な設計のようには見えません。 隣接リストのような別のアプローチを検討してください 。

    ソリューション#1-MySQL 8 JSONサポート:

    MySQL 8では、<を使用できます。 code> JSON_ARRAYAGG() および JSON_OBJECT() SQLのみでJSONの結果を取得するには:

    select json_object(
      'name', l1.level_1_name,
      'children', json_arrayagg(json_object('name', l2.level_2_name, 'children', l2.children))
    ) as json
    from level_1 l1
    left join (
      select l2.level_2_name
           , l2.level_1_fk
           , json_arrayagg(json_object('name', l3.level_3_name)) as children
      from level_2 l2
      left join level_3 l3 on l3.level_2_fk = l2.level_2_pk
      group by l2.level_2_pk
    ) l2 on l2.level_1_fk = l1.level_1_pk
    group by level_1_pk
    

    結果は次のとおりです。

    {"name": "Bob", "children": [{"name": "Ted", "children": [{"name": "Fred"}]}, {"name": "Carol", "children": [{"name": "Harry"}]}, {"name": "Alice", "children": [{"name": "Mary"}]}]}
    

    db-fiddleデモ

    フォーマット済み:

    {"name": "Bob", "children": [{"name": "Ted", "children": [{"name": "Fred"}]}, {"name": "Carol", "children": [{"name": "Harry"}]}, {"name": "Alice", "children": [{"name": "Mary"}]}]}
    

    ソリューション#2-GROUP_CONCAT()を使用したJSONの構築:

    名前に引用文字が含まれていない場合は、 GROUP_CONCAT()を使用して、古いバージョンでJSON文字列を手動で作成できます。 :

    $query = <<<MySQL
        select concat('{',
          '"name": ', '"', l1.level_1_name, '", ',
          '"children": ', '[', group_concat(
            '{',
            '"name": ', '"', l2.level_2_name, '", ',
            '"children": ', '[', l2.children, ']',
            '}'
          separator ', '), ']'        
        '}') as json
        from level_1 l1
        left join (
          select l2.level_2_name
               , l2.level_1_fk
               , group_concat('{', '"name": ', '"',  l3.level_3_name, '"', '}') as children
          from level_2 l2
          left join level_3 l3 on l3.level_2_fk = l2.level_2_pk
          group by l2.level_2_pk
        ) l2 on l2.level_1_fk = l1.level_1_pk
        group by level_1_pk
    MySQL;
    

    結果は同じになります(デモ を参照) )

    ソリューション#3-PHPオブジェクトを使用したネスト構造の構築:

    より単純なSQLクエリを記述し、PHPでネストされた構造を構築することもできます:

    $result = $connection->query("
        select level_1_name as name, null as parent
        from level_1
        union all
        select l2.level_2_name as name, l1.level_1_name as parent
        from level_2 l2
        join level_1 l1 on l1.level_1_pk = l2.level_1_fk
        union all
        select l3.level_3_name as name, l2.level_2_name as parent
        from level_3 l3
        join level_2 l2 on l2.level_2_pk = l3.level_2_fk
    ");
    

    結果は

    name    | parent
    ----------------
    Bob     | null
    Ted     | Bob
    Carol   | Bob
    Alice   | Bob
    Fred    | Ted
    Harry   | Carol
    Mary    | Alice
    

    デモ

    注:名前は、すべてのテーブルで一意である必要があります。しかし、重複が可能だった場合、どのような結果が期待できるかわかりません。

    次に、次の名前でインデックス付けされた配列のオブジェクトとして行を保存します:

    $data = []
    while ($row = $result->fetch_object()) {
        $data[$row->name] = $row;
    }
    

    $ data 含まれるようになります

    [
        'Bob'   => (object)['name' => 'Bob',   'parent' => NULL],
        'Ted'   => (object)['name' => 'Ted',   'parent' => 'Bob'],
        'Carol' => (object)['name' => 'Carol', 'parent' => 'Bob'],
        'Alice' => (object)['name' => 'Alice', 'parent' => 'Bob'],
        'Fred'  => (object)['name' => 'Fred',  'parent' => 'Ted'],
        'Harry' => (object)['name' => 'Harry', 'parent' => 'Carol'],
        'Mary'  => (object)['name' => 'Mary',  'parent' => 'Alice'],
    ]
    

    これで、ノードを1つのループでリンクできます。

    $roots = [];
    foreach ($data as $row) {
        if ($row->parent === null) {
            $roots[] = $row;
        } else {
            $data[$row->parent]->children[] = $row;
        }
        unset($row->parent);
    }
    
    echo json_encode($roots[0], JSON_PRETTY_PRINT);
    

    結果:

    {"name": "Bob", "children": [{"name": "Ted", "children": [{"name": "Fred"}]}, {"name": "Carol", "children": [{"name": "Harry"}]}, {"name": "Alice", "children": [{"name": "Mary"}]}]}
    

    デモ

    複数のルートノードが可能な場合( level_1_nameの複数の行 )、次に

    を使用します
    json_encode($roots);
    


    1. T-SQLで日付をフォーマットする方法

    2. MySQL正規表現は好きよりもはるかに遅い

    3. MySQLは、あるテーブルから別のテーブルにすべてのレコードを効率的にコピーします

    4. 複数のホストの許可を持つMysqlドロップユーザー