从两个表中检索数据的 optimized/best 方法是什么?
What is the optimized/best way to retrieve data from two tables?
我有两个 table:
post
table:
|post_id | post_title |
+--------+------------+
| 1 | Post 1 |
| 2 | Post 2 |
| 3 | Post 3 |
post_creator
table:
|post_id | creator |
+--------+---------+
| 1 | John |
| 1 | Smith |
| 1 | Mike |
| 2 | Bob |
| 3 | Peter |
| 3 | Brad |
当我加入这些 table 时,它看起来像这样。
SELECT *
FROM post p
JOIN post_creator c ON p.post_id = c.post_id
|post_id | post_title | post_id | creator|
+----------------------------------------+
| 1 | Post 1 | 1 | John |
| 1 | Post 1 | 1 | Smith |
| 1 | Post 1 | 1 | Mike |
| 2 | Post 2 | 2 | Bob |
| 3 | Post 3 | 3 | Peter |
| 3 | Post 3 | 3 | Brad |
我想和它的创作者一起抓住每一个 post。但是在这种情况下,由于创建者的原因,我的合并结果一次又一次地重复相同的 post。
我所做的是首先从 post table 中获取所有数据。然后我循环那个结果并在循环中我获取了每个 posts 的所有创建者。但在这种情况下,它会一次又一次地查询每个内容以获取创作者。
$sql = "SELECT * FROM post";
$stmt = $conn->prepare($sql);
$stmt->execute();
$res = $stmt->fetchAll(PDO::FETCH_OBJ);
$dataObj = new stdClass;
$dataArr = [];
foreach($res as $post){
$sql = "SELECT creator FROM post_creator WHERE post_id=$post->post_id";
$stmt = $conn->prepare($sql);
$stmt->execute();
$creators = $stmt->fetchAll(PDO::FETCH_OBJ);
$dataObj->post_id = $post->post_id
$dataObj->post_title = $post->title
$dataObj->creators = $creators;
array_push($dataArr, $dataObj);
}
所以我的 dataArr
终于有了这种结构。
[
{
post_id: 1,
post_title: Post 1,
creators:[John, Smith, Mike]
},
{
post_id: 2,
post_title: Post 2,
creators:[Bob]
},
{
post_id: 2,
post_title: Post 1,
creators:[Peter, Brad]
},
]
这就是我想要的。现在我可以循环它并渲染到一个视图。
有没有什么optimized/better方法可以得到这个结果而不用反复循环和查询?
我认为你需要使用 group_concat
来分组你的 creators
。
SELECT p.post_id, post_title, group_concat(creator)
FROM post p
JOIN post_creator using(post_id)
group by p.post_id
此外,这:
$sql = "SELECT creator FROM post_creator WHERE post_id=$post->post_id";
$stmt = $conn->prepare($sql);
$stmt->execute();
准备好的语句使用不当。应该写成:
$sql = "SELECT creator FROM post_creator WHERE post_id=?";
$stmt = $conn->prepare($sql);
$stmt->execute(array($post->post_id));
如果需要,但不需要。始终绑定值,永远不要直接放入 SQL.
我想说有 3 条不同的路可以走,每条路都有一些好处。
选项 1。使用 JOIN(和重叠行)的简单 SELECT 查询
这或多或少是您已经尝试过的,在您列出的第一个查询中;这导致了重复的行。
修改您的应用程序代码来处理欺骗是相当简单的,只需将创建者折叠到相同的 array/object。开销也几乎为零。从关系数据库设计的角度来看,这种方法仍然是最佳实践。
SELECT p.post_id
, p.post_title
, c.creator
FROM post p
LEFT JOIN post_creator c
ON p.post_id = c.post_id
ORDER BY p.post_id ASC
.
/* $rows = ...query...; */
$posts = [];
foreach ($rows as $row) {
if (!isset($posts[( $row['post_id'] )])) {
// this is a new post_id
$post = [];
$post['id'] = $row['post_id'];
$post['creators'] = [];
$post['creators'][] = $row['creator'];
$posts[( $row['post_id'] )] = $post;
} else {
// this is just an additional creator
$posts[( $row['post_id'] )]['creators'][] = $row['creator'];
}
}
选项 2. 多值列(数组或 json)
对于非纯粹主义者来说,一种稍微更实用的解决方案是让您的查询生成包含多个值的输出列。这通常意味着 JSON 或 ARRAY 列。具体细节取决于您选择的数据库系统。
无论哪种情况,您都可以将其与 SQL GROUP BY
功能结合使用。
假设您使用 MySQL 并且更喜欢 JSON 类型;然后你会进行如下查询:
SELECT p.post_id
, p.post_title
, JSON_ARRAYAGG(c.creator) AS creators
FROM post p
LEFT JOIN post_creator c
ON p.post_id = c.post_id
GROUP BY p.post_id
ORDER BY p.post_id ASC
这样,每个 post 您只会收到一条记录,并且您会得到一个值,例如 ['Mike', 'Paul', 'Susan']
,json_decode()
可以变成一个合适的 [=53] =]数组。
选项 3. 完整文件
另一种基于选项 #2 的替代方案是完全使用 JSON,并完全放弃关系记录集。
大多数现代 DBMS 具有大量 JSON 功能,您自己列为 dataArr
的格式可以由数据库完全生成以响应单个 SELECT
查询。
这样,查询将始终只产生 1 行和 1 列,它包含整个 dataArr
组合所有这些 posts(同样,可以变成本机PHP 数组或对象树 json_decode
,就像以前一样)。
虽然此方法的结果可能非常简洁(取决于您的应用程序的编写方式),但有些人可能想知道为什么您使用 RDBMS 而不是 MongoDB.
总体而言,我推荐选项 1。
我有两个 table:
post
table:
|post_id | post_title |
+--------+------------+
| 1 | Post 1 |
| 2 | Post 2 |
| 3 | Post 3 |
post_creator
table:
|post_id | creator |
+--------+---------+
| 1 | John |
| 1 | Smith |
| 1 | Mike |
| 2 | Bob |
| 3 | Peter |
| 3 | Brad |
当我加入这些 table 时,它看起来像这样。
SELECT *
FROM post p
JOIN post_creator c ON p.post_id = c.post_id
|post_id | post_title | post_id | creator|
+----------------------------------------+
| 1 | Post 1 | 1 | John |
| 1 | Post 1 | 1 | Smith |
| 1 | Post 1 | 1 | Mike |
| 2 | Post 2 | 2 | Bob |
| 3 | Post 3 | 3 | Peter |
| 3 | Post 3 | 3 | Brad |
我想和它的创作者一起抓住每一个 post。但是在这种情况下,由于创建者的原因,我的合并结果一次又一次地重复相同的 post。
我所做的是首先从 post table 中获取所有数据。然后我循环那个结果并在循环中我获取了每个 posts 的所有创建者。但在这种情况下,它会一次又一次地查询每个内容以获取创作者。
$sql = "SELECT * FROM post";
$stmt = $conn->prepare($sql);
$stmt->execute();
$res = $stmt->fetchAll(PDO::FETCH_OBJ);
$dataObj = new stdClass;
$dataArr = [];
foreach($res as $post){
$sql = "SELECT creator FROM post_creator WHERE post_id=$post->post_id";
$stmt = $conn->prepare($sql);
$stmt->execute();
$creators = $stmt->fetchAll(PDO::FETCH_OBJ);
$dataObj->post_id = $post->post_id
$dataObj->post_title = $post->title
$dataObj->creators = $creators;
array_push($dataArr, $dataObj);
}
所以我的 dataArr
终于有了这种结构。
[
{
post_id: 1,
post_title: Post 1,
creators:[John, Smith, Mike]
},
{
post_id: 2,
post_title: Post 2,
creators:[Bob]
},
{
post_id: 2,
post_title: Post 1,
creators:[Peter, Brad]
},
]
这就是我想要的。现在我可以循环它并渲染到一个视图。
有没有什么optimized/better方法可以得到这个结果而不用反复循环和查询?
我认为你需要使用 group_concat
来分组你的 creators
。
SELECT p.post_id, post_title, group_concat(creator)
FROM post p
JOIN post_creator using(post_id)
group by p.post_id
此外,这:
$sql = "SELECT creator FROM post_creator WHERE post_id=$post->post_id";
$stmt = $conn->prepare($sql);
$stmt->execute();
准备好的语句使用不当。应该写成:
$sql = "SELECT creator FROM post_creator WHERE post_id=?";
$stmt = $conn->prepare($sql);
$stmt->execute(array($post->post_id));
如果需要,但不需要。始终绑定值,永远不要直接放入 SQL.
我想说有 3 条不同的路可以走,每条路都有一些好处。
选项 1。使用 JOIN(和重叠行)的简单 SELECT 查询
这或多或少是您已经尝试过的,在您列出的第一个查询中;这导致了重复的行。
修改您的应用程序代码来处理欺骗是相当简单的,只需将创建者折叠到相同的 array/object。开销也几乎为零。从关系数据库设计的角度来看,这种方法仍然是最佳实践。
SELECT p.post_id
, p.post_title
, c.creator
FROM post p
LEFT JOIN post_creator c
ON p.post_id = c.post_id
ORDER BY p.post_id ASC
.
/* $rows = ...query...; */
$posts = [];
foreach ($rows as $row) {
if (!isset($posts[( $row['post_id'] )])) {
// this is a new post_id
$post = [];
$post['id'] = $row['post_id'];
$post['creators'] = [];
$post['creators'][] = $row['creator'];
$posts[( $row['post_id'] )] = $post;
} else {
// this is just an additional creator
$posts[( $row['post_id'] )]['creators'][] = $row['creator'];
}
}
选项 2. 多值列(数组或 json)
对于非纯粹主义者来说,一种稍微更实用的解决方案是让您的查询生成包含多个值的输出列。这通常意味着 JSON 或 ARRAY 列。具体细节取决于您选择的数据库系统。
无论哪种情况,您都可以将其与 SQL GROUP BY
功能结合使用。
假设您使用 MySQL 并且更喜欢 JSON 类型;然后你会进行如下查询:
SELECT p.post_id
, p.post_title
, JSON_ARRAYAGG(c.creator) AS creators
FROM post p
LEFT JOIN post_creator c
ON p.post_id = c.post_id
GROUP BY p.post_id
ORDER BY p.post_id ASC
这样,每个 post 您只会收到一条记录,并且您会得到一个值,例如 ['Mike', 'Paul', 'Susan']
,json_decode()
可以变成一个合适的 [=53] =]数组。
选项 3. 完整文件
另一种基于选项 #2 的替代方案是完全使用 JSON,并完全放弃关系记录集。
大多数现代 DBMS 具有大量 JSON 功能,您自己列为 dataArr
的格式可以由数据库完全生成以响应单个 SELECT
查询。
这样,查询将始终只产生 1 行和 1 列,它包含整个 dataArr
组合所有这些 posts(同样,可以变成本机PHP 数组或对象树 json_decode
,就像以前一样)。
虽然此方法的结果可能非常简洁(取决于您的应用程序的编写方式),但有些人可能想知道为什么您使用 RDBMS 而不是 MongoDB.
总体而言,我推荐选项 1。