Skip to content
Advertisement

MySQL – How to modify parent/child select query to add more children to existing array/JSON?

I have the following query working OK:

The resulting array, with some PHP, produces the following JSON, which is fine so far, showing the ‘strand’ children of ‘theme’ parents:

I now want to add the same children set: ‘Year 1’, ‘Year 2’, ‘Year 3’ and ‘Year 4’, from table strand.year, to each strand.strand_name parent (e.g. Professional Behaviours, Medical Ethics and Law etc).

I have tried the following modified query:

But as you can see below, the relationships now are incomplete; the first five nodes have lost their children, and only one strand, Information Literacy, has the Year children.

How should the query be changed to show all the relationships as in the first JSON, and add the set of four ‘Year X’ children to each strand?

Required JSON result up to Year children (ignore children of Year x

See fiddle for original query

SQL:

theme.sql

strand.sql

The working PHP/MySQL for the original version of the JSON is:

Advertisement

Answer

As I wrote in my other answer: “The name should be unique along all tables”. That was an assumption based on sample data from your previous question. But it’s not the case for the strand table. If a name occurs multiple times in the SQL result set, previous rows with same name will be overwritten here:

because $row->name has the same value. So you need a column as a unique identifier and use that column as index for the $data array. You can not use name because it’s not unique in the strand table. And you can not use the primary keys, because they are not unique along all tables. But you can use a combination of table name (or a unique table alias) and primary key like

The parent column should have the same pattern

The next problem is – How to group the strands by year per theme? The nesting logic doesn’t follow the pattern parentTable <- childTable <- grandChildTable. That would be condition <- theme <- year <- strand. Instead two levels (year and strand name) are in one table. You need to “extract” the years from the strand table with a DISTINCT query as if they were stored in a separate table. The unique identifier should be a combination of theme PK and year. The individual strands should reference those identifiers in the parent column. The final query would be like

db-fiddle

The result looks like

You see – “Teamwork and Leadership” appears twice. But the two rows have different global_id and different parent_global_id. You can also see how parent_global_id unambiguously references the global_id of a parent row.

The result is basically an adjacency list composed from data in different tables. These schema is quite simple to transform into a nested structure in PHP. The PHP code needs little changes to be adjusted to the new columns:

Notes:

  • The result will not be the same as in your link. But I don’t know how a strand row (like “Professional Behavours”) can be parent of other strand rows, without any according information in the data.
  • I replaced your comma join with an explicit CROSS JOIN, which makes the intention clearer. The assumption here is that there is only one row in the condition_theme_lookup table. Otherwise you will need a JOIN condition, which is not possible with the given schema.
  • You wrote in the comments: “There will be several more child levels in the final JSON”. All levels must follow the same nesting logic, or at least be convertible (like the case with the years). If you have more surprises, the solution might be not appropriate. At some point I would consider to execute one query per level and build the hierarchical structure “bottom-up” (from leafs to root).

MySQL 8 – CTE + JSON support

Using the combination of JSON_OBJECT() function, JSON_ARRAYAGG() aggregate function and common table expressions (CTE), we are now able to get a nested JSON result with multiple nesting levels with a single query:

db-fiddle

formatted result

Every nesting level is wrapped in its own CTE, which improves the readability. And every level can have its own nesting logic. Since the result is build step by step, it shouldn’t be a big deal to add more levels.

Update

To swap the levels of strands and years in the UNION query, only little changes are required in the last two subqueries:

db-fiddle

If you need the children of a node to be sorted in a specific way but differently for the levels, I would suggest to add two columns (num_sort and str_sort) to every subquery. For example if you want themes to be sorted by their PK – Add

If strand should be sorted by name – add

If years should be sorted by value but in a natural way (“Year 10” > “Year 2”)

Then append ORDER BY num_sort, str_sort to the query.

db-fiddle

You will then need to remove those columns (attributes) from the PHP objects

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement