[英]Populate results of one-to-many in a single query
我有兩個表: activity
和tag
。 一個activity
有很多tags
。 我需要優化當前的設置,我們在表中查找所有活動,然后遍歷每個活動以查找所有標記。 最后,我們將內容作為JSON處理。
這是一個例子:
-- Query that we execute one time
SELECT `id`, `name`
FROM `activity`
-- Query that we execute N times
SELECT `id`, `name`, `color`
FROM `tag`
WHERE `tag`.`activityId` = $activityId
所以我們的最終代碼看起來像這樣:
$result = mysqli_query("
SELECT `id`, `name`
FROM `activity`
");
$data = array();
for ($i=0; $i<mysqli_num_rows($result); $i++) {
$row = mysqli_fetch_assoc($result);
$data[$i]["activityId"] = $row["id"];
$data[$i]["name"] = $row["name"];
$data[$i]["tags"] = array();
$activityId = $row["id"];
$resultTags = mysqli_query("
SELECT `id`, `name`, `color`
FROM `tag`
WHERE `tag`.`activityId` = $activityId
");
for ($j=0; $j<mysqli_num_rows($resultTags); $j++) {
$row = mysqli_fetch_assoc($resultTags);
$data[$i]["tags"][$j]["name"] = $row["name"];
$data[$i]["tags"][$j]["color"] = $row["color"];
}
}
PS:這是一個偽代碼,我們的查詢和表格更復雜,但這基本上是我們問題的本質。
是否有可能使用內部查詢來實現,甚至更好,僅使用連接?
由於我們需要填充5個表,我們最終會為每個活動執行5個查詢。
謝謝。
當然只需JOIN
活動表上的標簽表即可。
您的查詢應該看起來像這樣。
SELECT activity.id, activity.name, tag.name AS tagName, tag.color AS tagColor
FROM activity
JOIN tag ON tag.activityId = activity.id;
活動表的主鍵是id
並且每行都是唯一的(即,對於任何給定的activity.id
,名稱永遠不會改變),即使沒有標簽,JOIN也是安全的。 我們會回來至少N
行,其中N
是多少tag.activityId
有相應的行id
的activity
。
所以在PHP中,您可以使用像這樣的單個查詢來構建數組。
$data = [];
foreach($result as $row) {
$tags = ["name" => $row["tagName"], "color" => $row["tagColor"]];
if (isset($data[$row["id"]])) {
$data[$row["id"]]["tags"][] = $tags;
} else {
$data[$row["id"]] = [
[
"id" => $row["id"],
"name" => $row["name"],
"tags" => [$tags],
],
];
}
}
因為活動表中的信息永遠不會改變,所以我們唯一需要追加的是標記,我們通過在循環中檢查isset($data[$row["id"]])
來做。
因此,從這個SQLFiddle可以看到兩個表的工作示例,其中我們在activity
有4行,在tag
表中有6行。 這給了我們總共6行。 由於活動表中的一個ID沒有任何標記,因此將其從結果集中排除。 要獲得帶有0個標簽的行,您可以使用LEFT JOIN
就像這樣 。
SELECT activity.id, activity.name,
tag.activityId AS tagId, tag.name AS tagName, tag.color AS tagColor
FROM activity
LEFT JOIN tag ON tag.activityId = activity.id;
這意味着我們為空標記行獲取空值。 所以我們可以修改PHP看起來像這樣。
$data = [];
foreach($result as $row) {
$tags = empty($row["tagId"]) ?: ["name" => $row["tagName"], "color" => $row["tagColor"]];
if (isset($data[$row["id"]]) && $tags) {
$data[$row["id"]]["tags"][] = $tags;
} else {
$data[$row["id"]] = [
[
"id" => $row["id"],
"name" => $row["name"],
"tags" => $tags ? [$tags] : [],
],
];
}
}
因此,使用json_encode
從PHP獲得的最終輸出,以及SQLFiddle中的示例數據,應該看起來像這樣。
"1": { "id": 1, "name": "foo", "tags": [ { "tagName": "tag-a", "tagColor": "red" }, { "tagName": "tag-b", "tagColor": "green" }, { "tagName": "tag-c", "tagColor": "blue" } ] }, "2": { "id": 2, "name": "bar", "tags": [ { "tagName": "tag-a", "tagColor": "orange" }, { "tagName": "tag-b", "tagColor": "red" } ] }, "3": { "id": 3, "name": "baz", "tags": [ { "tagName": "tag-a", "tagColor": "purple" } ] }, "4": { "id": 4, "name": "quix", "tags": [] } }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.