![](/img/trans.png)
[英]How to optimize the SQL query and add more array elements to the existing array?
[英]MySQL - How to modify parent/child select query to add more children to existing array/JSON?
我有以下查詢工作正常:
SELECT core_condition AS name, NULL AS parent
FROM condition_theme_lookup
UNION ALL
SELECT theme_name AS name, condition_theme_lookup.core_condition AS parent
FROM theme, condition_theme_lookup
UNION ALL
SELECT strand.strand_name AS name, theme.theme_name AS parent
FROM strand
JOIN theme ON theme.theme_pk = strand.theme_fk
帶有一些PHP的結果數組將生成以下JSON,到目前為止,此JSON還不錯,它顯示了'theme'父級的'strand'子級:
{
"name": "Condition",
"children": [{
"name": "Professional",
"children": [{
"name": "Professional Behavours"
}, {
"name": "Self-Care and Self-Awareness"
}, {
"name": "Medical Ethics and Law"
}]
}, {
"name": "Leader",
"children": [{
"name": "Teamwork and Leadership"
}, {
"name": "Collaborative Practice"
}, {
"name": "Health Systems and Careers"
}]
}, {
"name": "Advocate",
"children": [{
"name": "Health Advocacy"
}, {
"name": "Aboriginal Health"
}, {
"name": "Diversity and Inequality"
}, {
"name": "Health Promotion"
}]
}, {
"name": "Clinician",
"children": [{
"name": "Scientific Knowledge"
}, {
"name": "Patient Assessment and Clinical Reasoning"
}, {
"name": "Patient Management"
}, {
"name": "Patient Perspective"
}, {
"name": "Clinical Communication"
}, {
"name": "Quality Care"
}]
}, {
"name": "Educator",
"children": [{
"name": "Life-Long Learning"
}, {
"name": "Mentoring Relationships"
}, {
"name": "Patient Education"
}, {
"name": "Teaching and Learning"
}, {
"name": "Assessment and Evaluation"
}]
}, {
"name": "Scholar",
"children": [{
"name": "Research and Biostatistics"
}, {
"name": "Evidence-Based Practice"
}, {
"name": "Information Literacy"
}]
}]
}
我現在想將相同的子集:從表strand.year
到每個strand.strand_name
父級的“ Year 1”,“ Year 2”,“ Year 3”和“ Year 4”(例如,職業行為,醫學道德和法律等)。
我嘗試了以下修改后的查詢:
SELECT core_condition AS name, NULL AS parent
FROM condition_theme_lookup
UNION ALL
SELECT theme_name AS name, condition_theme_lookup.core_condition AS parent
FROM theme, condition_theme_lookup
UNION ALL
SELECT strand.strand_name AS name, theme.theme_name AS parent
FROM strand, theme
UNION ALL
SELECT strand.year AS name, strand.strand_name AS parent
FROM strand
JOIN theme ON theme.theme_pk = strand.theme_fk
但是,如您在下面看到的,現在的關系是不完整的。 前五個節點失去了孩子,只有一串信息素養擁有Year兒童。
{
"name": null,
"children": [{
"name": "Professional"
}, {
"name": "Leader"
}, {
"name": "Advocate"
}, {
"name": "Clinician"
}, {
"name": "Educator"
}, {
"name": "Scholar",
"children": [{
"name": "Professional Behavours"
}, {
"name": "Self-Care and Self-Awareness"
}, {
"name": "Teamwork and Leadership"
}, {
"name": "Collaborative Practice"
}, {
"name": "Health Systems and Careers"
}, {
"name": "Health Advocacy"
}, {
"name": "Aboriginal Health"
}, {
"name": "Diversity and Inequality"
}, {
"name": "Health Promotion"
}, {
"name": "Scientific Knowledge"
}, {
"name": "Patient Assessment and Clinical Reasoning"
}, {
"name": "Patient Management"
}, {
"name": "Patient Perspective"
}, {
"name": "Clinical Communication"
}, {
"name": "Quality Care"
}, {
"name": "Life-Long Learning"
}, {
"name": "Mentoring Relationships"
}, {
"name": "Patient Education"
}, {
"name": "Teaching and Learning"
}, {
"name": "Assessment and Evaluation"
}, {
"name": "Research and Biostatistics"
}, {
"name": "Evidence-Based Practice"
}, {
"name": "Information Literacy",
"children": [{
"name": "Year 1"
}, {
"name": "Year 2"
}, {
"name": "Year 3"
}, {
"name": "Year 4"
}]
}, {
"name": "Medical Ethics and Law"
}]
}]
}
如何更改查詢以顯示第一個JSON中的所有關系,並向每個鏈中添加四個“ Year X”子級的集合?
所需的JSON結果,直到Year子級為止(忽略Year x的子級
請參閱小提琴以獲取原始查詢
SQL:
適用於JSON原始版本的PHP / MySQL是:
$condition = $_POST['condition'];
$query = "SELECT core_condition AS name, NULL AS parent
FROM condition_theme_lookup
UNION ALL
SELECT theme_name AS name, condition_theme_lookup.core_condition AS parent
FROM theme, condition_theme_lookup
UNION ALL
SELECT strand.strand_name AS name, theme.theme_name AS parent
FROM strand
JOIN theme ON theme.theme_pk = strand.theme_fk";
$result = $connection->query($query);
$data = array();
while ($row = $result->fetch_object()) {
$data[$row->name] = $row;
}
foreach ($data as $row) {
if ($row->name == 'Condition') {
$row->name = $condition;
}
if ($row->parent === null) {
$roots[]= $row;
} else {
$data[$row->parent]->children[] = $row;
}
unset($row->parent);
}
$json = json_encode($roots);
正如我在其他答案中所寫的那樣:“名稱在所有表中都應該是唯一的”。 那是根據您先前問題的樣本數據得出的假設。 但這不是strand
的情況。 如果一個名稱在SQL結果集中多次出現,則具有相同名稱的先前行將在此處被覆蓋:
$data[$row->name] = $row;
因為$row->name
具有相同的值。 因此,您需要使用一列作為唯一標識符,並將該列用作$data
數組的索引。 您不能使用name
因為它在strand
中不是唯一的。 而且您不能使用主鍵,因為它們在所有表中都不唯一。 但是您可以結合使用表名(或唯一的表別名)和主鍵,例如
CONCAT('condition:', condition_theme_lookup_pk) AS global_id
...
CONCAT('theme:', theme_pk) AS global_id
....
CONCAT('strand:', strand_pk) AS global_id
parent
列應具有相同的模式
CONCAT('theme:', theme_fk) AS parent_global_id
下一個問題是-如何按每個主題的年份對子線進行分組? 嵌套邏輯不遵循模式parentTable <- childTable <- grandChildTable
。 那將是condition <- theme <- year <- strand
。 而是在一個表中有兩個級別(年份和鏈名稱)。 您需要使用DISTINCT查詢從strand
中“提取”年份,就像它們存儲在單獨的表中一樣。 唯一標識符應該是主題PK和年份的組合。 各個鏈應該在父列中引用這些標識符。 最終查詢將是
SELECT CONCAT('condition:', condition_theme_lookup_pk) AS global_id,
core_condition AS name,
NULL AS parent_global_id
FROM condition_theme_lookup
UNION ALL
SELECT CONCAT('theme:', theme_pk) AS global_id,
theme_name AS name,
CONCAT('condition:', condition_theme_lookup_pk) AS parent_global_id
FROM theme CROSS JOIN condition_theme_lookup
UNION ALL
SELECT DISTINCT
CONCAT('theme:', theme_fk, ',year:', strand.year) AS global_id,
strand.year AS name,
CONCAT('theme:', theme_fk) AS parent_global_id
FROM strand
UNION ALL
SELECT CONCAT('strand:', strand_pk) AS global_id,
strand.strand_name AS name,
CONCAT('theme:', theme_fk, ',year:', strand.year) AS parent_global_id
FROM strand
結果看起來像
global_id | name | parent_global_id
--------------------|------------------------------|---------------------
condition:1 | Condition | null
theme:1 | Professional | condition:1
theme:2 | Leader | condition:1
...
theme:1,year:Year 1 | Year 1 | theme:1
theme:2,year:Year 1 | Year 1 | theme:2
...
theme:1,year:Year 2 | Year 2 | theme:1
theme:2,year:Year 2 | Year 2 | theme:2
...
strand:1 | Professional Behavours | theme:1,year:Year 1
strand:2 | Self-Care and Self-Awareness | theme:1,year:Year 1
strand:3 | Teamwork and Leadership | theme:2,year:Year 1
strand:4 | Collaborative Practice | theme:2,year:Year 1
...
strand:27 | Teamwork and Leadership | theme:2,year:Year 2
您會看到-“團隊合作和領導力”出現了兩次。 但是這兩行具有不同的global_id
和不同的parent_global_id
。 您還可以看到parent_global_id
如何明確引用父行的global_id
。
結果基本上是一個由不同表中的數據組成的鄰接表。 這些模式很容易轉換成PHP中的嵌套結構。 PHP代碼幾乎不需要更改即可調整為新列:
$result = $connection->query($query);
$data = array();
while ($row = $result->fetch_object()) {
$data[$row->global_id] = $row;
}
$roots = [];
foreach ($data as $row) {
if ($row->name == 'Condition') {
$row->name = $condition;
}
if ($row->parent_global_id === null) {
$roots[]= $row;
} else {
$data[$row->parent_global_id]->children[] = $row;
}
unset($row->parent_global_id);
unset($row->global_id);
}
$json = json_encode($roots);
筆記:
CROSS JOIN
替換了您的逗號CROSS JOIN
,這使意圖更加清晰。 這里的假設是condition_theme_lookup
表中只有一行。 否則,您將需要一個JOIN條件,這對於給定的架構是不可能的。 通過結合使用JSON_OBJECT()
函數, JSON_ARRAYAGG()
聚合函數和公用表表達式(CTE),我們現在可以通過單個查詢獲得具有多個嵌套級別的嵌套JSON結果:
with years as (
select
theme_fk,
year,
json_arrayagg(json_object('name', strand_name)) as children
from strand
group by theme_fk, year
), themes as (
select
t.theme_pk,
t.theme_name as name,
json_arrayagg(json_object('name', year, 'children', children)) as children
from theme t
left join years y on y.theme_fk = t.theme_pk
group by t.theme_pk
)
select json_object(
'name', c.core_condition,
'children', json_arrayagg(json_object('name', t.name, 'children', t.children))
) as json
from condition_theme_lookup c
cross join themes t
group by c.condition_theme_lookup_pk
每個嵌套級別都包裝在其自己的CTE中,從而提高了可讀性。 每個級別都可以有自己的嵌套邏輯。 由於結果是逐步構建的,因此添加更多級別並不重要。
要交換UNION查詢中的股數和年份,在最后兩個子查詢中只需要很少的更改:
...
SELECT DISTINCT
CONCAT('theme:', theme_fk, ',strand:', strand_name) AS global_id,
strand_name AS name,
CONCAT('theme:', theme_fk) AS parent_global_id
FROM strand
UNION ALL
SELECT CONCAT('strand_year:', strand_pk) AS global_id,
strand.year AS name,
CONCAT('theme:', theme_fk, ',strand:', strand_name) AS parent_global_id
FROM strand
如果您需要以特定的方式對節點的子級進行排序,但對於級別進行不同的排序,則建議為每個子查詢添加兩列( num_sort
和str_sort
)。 例如,如果您希望主題按其PK排序-添加
theme_pk as num_sort, '' as str_sort
如果應按名稱對鏈進行排序-添加
0 as num_sort, strand_name as str_sort
如果年份應按值自然排序(“ 10年級”>“ 2年級”)
cast(replace(year, 'Year ', '') as signed) as num_sort, '' as str_sort
然后將ORDER BY num_sort, str_sort
附加到查詢中。
然后,您需要從PHP對象中刪除這些列(屬性)
unset($row->parent_global_id);
unset($row->global_id);
unset($row->num_sort);
unset($row->str_sort);
如果您確切知道要使用的值(年),則可以在查詢中創建(偽)它們:
SELECT *, 'Year 1' as year1, 'Year 2' as year2 from strands... and so on
當您嘗試向原始查詢中添加額外的部分時-應該在“ JOIN”部分之后而不是之前進行。 “ JOIN”屬於先前的查詢。 這個版本應該工作:
SELECT core_condition AS name, NULL AS parent
FROM condition_theme_lookup
UNION ALL
SELECT theme_name AS name, condition_theme_lookup.core_condition AS parent
FROM theme, condition_theme_lookup
UNION ALL
SELECT strand.strand_name AS name, theme.theme_name AS parent
FROM strand
JOIN theme ON theme.theme_pk = strand.theme_fk
-- beginning of added query --
UNION ALL
SELECT strand.year AS name, strand.strand_name AS parent
FROM strand WHERE strand.year is not NULL;
我還添加了條件“ WHEREstrand.year不為NULL”-如果您確定所有記錄都設置了年份,請跳過此部分。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.