簡體   English   中英

在單個查詢中填充一對多的結果

[英]Populate results of one-to-many in a single query

我有兩個表: activitytag 一個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有相應的行idactivity

所以在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.

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