簡體   English   中英

MySQL-如何修改父項/子項選擇查詢以將更多子項添加到現有數組/ JSON?

[英]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:

theme.sql

strand.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

DB-小提琴

結果看起來像

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

筆記:

  • 結果將與您的鏈接中的結果不同。 但是我不知道在沒有數據的任何相關信息的情況下,鋼絞線行(例如“ Professional Behavours”)如何成為其他鋼絞線行的父級。
  • 我用顯式的CROSS JOIN替換了您的逗號CROSS JOIN ,這使意圖更加清晰。 這里的假設是condition_theme_lookup表中只有一行。 否則,您將需要一個JOIN條件,這對於給定的架構是不可能的。
  • 您在評論中寫道:“最終的JSON中將有幾個子級別”。 所有級別都必須遵循相同的嵌套邏輯,或者至少是可轉換的(例如年份)。 如果您有更多驚喜,則該解決方案可能不合適。 在某個時候,我會考慮對每個級別執行一個查詢,並構建“自下而上”的層次結構(從葉子到根)。

MySQL 8-CTE + JSON支持

通過結合使用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

DB-小提琴

格式化結果

每個嵌套級別都包裝在其自己的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

DB-小提琴

如果您需要以特定的方式對節點的子級進行排序,但對於級別進行不同的排序,則建議為每個子查詢添加兩列( num_sortstr_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附加到查詢中。

DB-小提琴

然后,您需要從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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM