繁体   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