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