I have two tables: activity
and tag
. One activity
have many tags
. I need to optimize our current setup, where we find all activities inside the table and then iterate over each activity to find all tags. Finally we dispose the content as JSON.
Here is an example:
-- 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
So our final code looks something like that:
$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: this is a pseudo-code, our queries and tables are way more complex, but that's basically the essence of our problem.
Is it possible to accomplish that using inner queries, or even better, just with joins?
Since we need to populate around 5 tables, we end up executing 5 queries for each activity we have.
Thank you.
Sure just JOIN
the tag table on the activity table.
Your query should look something like this.
SELECT activity.id, activity.name, tag.name AS tagName, tag.color AS tagColor
FROM activity
JOIN tag ON tag.activityId = activity.id;
The idea that the activity table's primary key is id
and that each row is unique (ie the name will never change for any given activity.id
), then the JOIN is safe even if the there are no tags. We'll get back at least N
rows where N
is the number of tag.activityId
rows that have a corresponding id
in activity
.
So in PHP you can build the array with a single query like this.
$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],
],
];
}
}
Because the information in the activity table will never change, then the only thing we need to append is the tags, which we do by checking isset($data[$row["id"]])
inside the loop.
So from this SQLFiddle you can see a working example of the two tables where we have 4 rows in the activity
, and 6 rows in the tag
table. This gives us a total of 6 rows. Since one of the ids in the activity table doesn't have any tags it's excluded from the result set. To get back rows with 0 tags you can use a LEFT JOIN
instead like this .
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;
Which means we get null values for empty tag rows. So we can modify the PHP to look like this.
$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] : [],
],
];
}
}
So your finally output from PHP using json_encode
, with the sample data in the SQLFiddle, should look something like this.
"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": [] } }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.