sql >> Database >  >> RDS >> Mysql

Hiërarchische JSON maken van MySQL-resultaten en PHP voor de D3.js-boom?

Dit lijkt geen fatsoenlijk ontwerp voor hiërarchische gegevens. Overweeg een andere benadering zoals aangrenzende lijst .

Oplossing #1 - MySQL 8 JSON-ondersteuning:

Met MySQL 8 kunt u JSON_ARRAYAGG() en JSON_OBJECT() om het JSON-resultaat alleen met SQL te krijgen:

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

Het resultaat is:

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

db-fiddle-demo

Opgemaakt:

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

Oplossing #2 - JSON bouwen met GROUP_CONCAT():

Als de namen geen aanhalingstekens bevatten, kunt u de JSON-tekenreeks in oudere versies handmatig samenstellen met GROUP_CONCAT() :

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

Het resultaat zou hetzelfde zijn (zie demo )

Oplossing #3 - Nestet-structuur maken met PHP-objecten:

Je kunt ook een eenvoudiger SQL-query schrijven en de geneste structuur in PHP maken:

$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
");

Het resultaat is

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

demo

Opmerking:de naam moet uniek zijn voor alle tabellen. Maar ik weet niet welk resultaat je zou verwachten als duplicaten mogelijk waren.

Sla nu de rijen op als objecten in een array die is geïndexeerd op de naam:

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

$data bevat nu

[
    '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'],
]

We kunnen nu de knooppunten in een enkele lus koppelen:

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

Het resultaat:

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

demo

Als meerdere hoofdknooppunten mogelijk zijn (meerdere rijen in level_1_name ), gebruik dan

json_encode($roots);


  1. Opmerkingen over PostgreSQL B-Tree-indexen

  2. MySQL toont telling van 0 voor datums zonder records

  3. Hoe het maximale aantal verbindingen in MySQL te vergroten?

  4. Laravel:hoe verwijder je rijen uit meerdere tabellen met dezelfde id met slechts 1 query?