[英]What is the optimized/best way to retrieve data from two tables?
我有兩張桌子。
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 |
當我加入這些表時,它看起來像這樣。
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 表中獲取所有數據。 然后我循環該結果並在循環內獲取每個帖子的所有創建者。 但在這種情況下,它會一次又一次地查詢每個內容以獲取創建者。
$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]
},
]
這就是我想要的。 現在我可以循環它並渲染到一個視圖。
有沒有優化/更好的方法來獲得這個結果,而無需一次又一次地循環和查詢?
我認為您需要使用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 條不同的道路,它們都有一些好處。
這或多或少是您已經嘗試過的,您列出的第一個查詢; 這導致重復的行。
修改你的應用程序代碼來處理欺騙是相當簡單的,只需將創建者折疊到同一個數組/對象中。 開銷也幾乎為零。 從關系數據庫設計的角度來看,這種方法仍然是最佳實踐。
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'];
}
}
對於非純粹主義者來說,更實用的解決方案是讓您的查詢生成包含多個值的 output 列。 這通常意味着 JSON 或 ARRAY 列。 確切的細節取決於您選擇的數據庫系統。
無論哪種情況,您都可以將其與 SQL GROUP BY
功能結合使用。 假設您使用 MySQL 並且更喜歡 JSON 類型; 然后,您將使用以下查詢 go :
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
這樣,每個帖子您只會收到一條記錄,並且您將獲得諸如['Mike', 'Paul', 'Susan']
類的值, json_decode()
可以將其轉換為正確的 PHP 數組。
另一種基於選項 #2 的替代方案是 go 完全與 JSON,並完全放棄關系記錄集。
大多數現代 DBMS 具有大量 JSON 功能和您自己列為dataArr
的格式,可以由數據庫完全生成以響應單個SELECT
查詢。
這樣,查詢將始終只產生 1 行 1 列,其中包含結合所有這些帖子的整個dataArr
(再次,可以將其轉換為原生 PHP 數組或 object 樹與json_decode
,就像以前一樣)。
雖然這種方法的結果可能非常簡潔(取決於您的應用程序的編寫方式),但有些人可能想知道為什么您使用的是 RDBMS 而不是 MongoDB 之類的東西。
總的來說,我會推薦選項 1。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.