在 PHP/MySql 中使用单个查询从多个表中获取 'related' 数据?
get 'related' data from several tables with a single query in PHP/MySql?
我正在开发一个小型 PHP 应用程序,从头开始到 practice/learn。目标是能够创建文章。每篇文章都可以有标签和图像,存储在它们自己的 table 中。
由于一篇文章可以有很多标签,而一个标签可以出现在多篇文章中,我还创建了一个 'tag/article' 关联 table.
现在,当我想编辑或显示一篇文章时,我通过执行多个数据库请求来获取它的数据(加上标签和图像)...例如,给定一个文章 ID,我会做类似的事情:
$article = getArticle($articleId);
$tags = getArticleTags($articleId);
$images = getArticleImages($articleId);
它有效,但我不确定它是否是一种干净的方法和良好的性能。
所以我想知道,这可以通过使用单个查询来完成吗?
我尝试过,但对 UNION 和 JOIN 有点困惑。
如果没有,是否有任何 better/recommended 方法可以实现此目的?
我使用的(简化)table是:
文章table:
┌-----┬------------------┬------------------------┐
│ id │ title │ dateCreated │
├-----┼------------------┼------------------------┤
│ 1 │ some article │ 2015-03-17 12:38:49 │
├-----┼------------------┼------------------------┤
│ 2 │ article two │ 2015-03-17 15:00:24 │
└-----┴------------------┴------------------------┘
标签table:
┌-----┬------------------┬------------------------┐
│ id │ name │ dateCreated │
├-----┼------------------┼------------------------┤
│ 1 │ php │ 2015-03-17 15:01:05 │
├-----┼------------------┼------------------------┤
│ 2 │ jquery │ 2015-03-17 15:24:12 │
└-----┴------------------┴------------------------┘
标签-文章关联 table:
┌-----┬--------┬-------------┬------------------------┐
│ id │ tagId │ articleId │ dateCreated │
├-----┼--------┼-------------┼------------------------┤
│ 1 │ 1 │ 1 │ 2015-03-17 15:01:06 │
├-----┼--------┼-------------┼------------------------┤
│ 2 │ 2 │ 1 │ 2015-03-17 15:24:58 │
├-----┼--------┼-------------┼------------------------┤
│ 3 │ 1 │ 2 │ 2015-03-17 15:30:38 │
└-----┴--------┴-------------┴------------------------┘
文章图片table:
┌-----┬-----------┬-------------┬------------------------┐
│ id │ fileName │ articleId │ dateCreated │
├-----┼-----------┼-------------┼------------------------┤
│ 1 │ pic1.jpg │ 1 │ 2015-03-17 16:05:26 │
├-----┼-----------┼-------------┼------------------------┤
│ 2 │ pic2.jpg │ 1 │ 2015-03-17 16:06:29 │
├-----┼-----------┼-------------┼------------------------┤
│ 3 │ dog.jpg │ 2 │ 2015-03-17 17:00:14 │
├-----┼-----------┼-------------┼------------------------┤
│ 4 │ cat.jpg │ 2 │ 2015-03-17 17:01:06 │
├-----┼-----------┼-------------┼------------------------┤
│ 5 │ bird.jpg │ 2 │ 2015-03-17 17:02:34 │
└-----┴-----------┴-------------┴------------------------┘
此外,我正在以这种方式使用 PDO,例如,获取一篇文章:
public function getArticleById($id){
$sql = "SELECT * FROM articles WHERE id=:id LIMIT 1";
$query = $this->db->prepare($sql);
$parameters = array(':id' => $id);
$result = $query->execute($parameters);
return ($result) ? $query->fetch() : 0;
}
任何输入将不胜感激。谢谢!
更新:
@Adrian 的回答接近我正在寻找的内容并部分解决了问题。它returns以下
问题是 id 列不明确,我无法弄清楚如何最终得到这样一个干净的数组,理想情况下:
Array
(
[title] => some article
[id] => 1
[tags] => Array
(
[0] => Array
(
[id] => 1
[name] => php
)
[1] => Array
(
[id] => 2
[name] => jquery
)
)
[images] => Array
(
[0] => Array
(
[id] => 1
[fileName] => pic1.jpg
)
[1] => Array
(
[id] => 2
[fileName] => pic2.jpg
)
)
)
然后,结合 Daniel 和 Adrian 的回答,我得到了这个查询,returns 更具可读性(我猜)我可以处理以实现上述数组(我想迭代和删除重复项):
SELECT
a.id,
a.title,
a.dateCreated as articleDate,
i.id as imageId,
i.fileName as imageFile,
t.id as tagId,
t.name as tagName
FROM
articles a
JOIN
article_tags ta ON a.id = ta.articleId
JOIN
tags t ON ta.tagId = t.id
JOIN
images i ON a.id = i.articleId
WHERE
a.id = 1
这个returns:
性能方面,因为这个可能的解决方案 returns 许多行具有重复数据(取决于与文章关联的标签和图像的数量)。 这比我开始时进行的初始多次调用更好吗?
SELECT
a.id,
a.title,
a.dateCreated as articleDate,
i.fileName as imageFile,
t.name as tagName
FROM
articles a
JOIN
images i ON a.id = i.articleId
JOIN
tag_article ta ON a.id = ta.articleId
JOIN
tags t ON ta.articleId = t.id
WHERE
a.id = ?
试试这个代码:
编辑:
$sql = "SELECT
articles.id,
articles.title,
articles.dateCreated as articleDate,
article-images.id as imageId,
article-images.fileName as imageFile,
tags.id as tagId,
tags.name as tagName
FROM articles
JOIN tag-article ON articles.id = tag-article.articleId
JOIN tags ON tag-article.tagId = tags.id
JOIN article-images ON article-images.articleId = articles.id
WHERE articles.id=:id"
$query = $this->db->prepare($sql);
$parameters = array(':id' => $id);
$result = $query->execute($parameters);
return ($result) ? $query->fetch() : 0;
?>
我找到了一种方法来实现我想要的,方法是结合@Adrian 和@Daniel 的回答 post,他们为我指明了正确的方向(谢谢!)并添加了 CONCAT 的使用, GROUP_CONCAT 和 CAST(混合 int 和 varchar)。
我对这两个答案都投了赞成票,这部分解决了问题,但我认为我应该 post 我自己的答案,因为(我认为)是一个更清晰的解决方案,至少对于我想要的,它可能会有所帮助别人。
这是SQL
SELECT
a.id,
a.title,
GROUP_CONCAT( DISTINCT CAST( t.id AS CHAR( 50 ) ) , ':', t.name ORDER BY t.id SEPARATOR ',' ) AS 'tags',
GROUP_CONCAT( DISTINCT CAST( i.id AS CHAR( 50 ) ) , ':', i.fileName ORDER BY i.id SEPARATOR ',' ) AS 'images'
FROM
articles a
JOIN
article_tags ta ON a.id = ta.articleId
JOIN
images i ON a.id = i.articleId
JOIN
tags t ON ta.tagId = t.id
WHERE
a.id = :id
此查询 return 如下,id = 1:
包含文章数据、标签和图像的单行 id:value 格式,我认为在 PHP 中解析起来非常容易,甚至可以用于 return json 字符串,只需添加另一个 concat。
我正在开发一个小型 PHP 应用程序,从头开始到 practice/learn。目标是能够创建文章。每篇文章都可以有标签和图像,存储在它们自己的 table 中。 由于一篇文章可以有很多标签,而一个标签可以出现在多篇文章中,我还创建了一个 'tag/article' 关联 table.
现在,当我想编辑或显示一篇文章时,我通过执行多个数据库请求来获取它的数据(加上标签和图像)...例如,给定一个文章 ID,我会做类似的事情:
$article = getArticle($articleId);
$tags = getArticleTags($articleId);
$images = getArticleImages($articleId);
它有效,但我不确定它是否是一种干净的方法和良好的性能。 所以我想知道,这可以通过使用单个查询来完成吗? 我尝试过,但对 UNION 和 JOIN 有点困惑。 如果没有,是否有任何 better/recommended 方法可以实现此目的?
我使用的(简化)table是:
文章table:
┌-----┬------------------┬------------------------┐
│ id │ title │ dateCreated │
├-----┼------------------┼------------------------┤
│ 1 │ some article │ 2015-03-17 12:38:49 │
├-----┼------------------┼------------------------┤
│ 2 │ article two │ 2015-03-17 15:00:24 │
└-----┴------------------┴------------------------┘
标签table:
┌-----┬------------------┬------------------------┐
│ id │ name │ dateCreated │
├-----┼------------------┼------------------------┤
│ 1 │ php │ 2015-03-17 15:01:05 │
├-----┼------------------┼------------------------┤
│ 2 │ jquery │ 2015-03-17 15:24:12 │
└-----┴------------------┴------------------------┘
标签-文章关联 table:
┌-----┬--------┬-------------┬------------------------┐
│ id │ tagId │ articleId │ dateCreated │
├-----┼--------┼-------------┼------------------------┤
│ 1 │ 1 │ 1 │ 2015-03-17 15:01:06 │
├-----┼--------┼-------------┼------------------------┤
│ 2 │ 2 │ 1 │ 2015-03-17 15:24:58 │
├-----┼--------┼-------------┼------------------------┤
│ 3 │ 1 │ 2 │ 2015-03-17 15:30:38 │
└-----┴--------┴-------------┴------------------------┘
文章图片table:
┌-----┬-----------┬-------------┬------------------------┐
│ id │ fileName │ articleId │ dateCreated │
├-----┼-----------┼-------------┼------------------------┤
│ 1 │ pic1.jpg │ 1 │ 2015-03-17 16:05:26 │
├-----┼-----------┼-------------┼------------------------┤
│ 2 │ pic2.jpg │ 1 │ 2015-03-17 16:06:29 │
├-----┼-----------┼-------------┼------------------------┤
│ 3 │ dog.jpg │ 2 │ 2015-03-17 17:00:14 │
├-----┼-----------┼-------------┼------------------------┤
│ 4 │ cat.jpg │ 2 │ 2015-03-17 17:01:06 │
├-----┼-----------┼-------------┼------------------------┤
│ 5 │ bird.jpg │ 2 │ 2015-03-17 17:02:34 │
└-----┴-----------┴-------------┴------------------------┘
此外,我正在以这种方式使用 PDO,例如,获取一篇文章:
public function getArticleById($id){
$sql = "SELECT * FROM articles WHERE id=:id LIMIT 1";
$query = $this->db->prepare($sql);
$parameters = array(':id' => $id);
$result = $query->execute($parameters);
return ($result) ? $query->fetch() : 0;
}
任何输入将不胜感激。谢谢!
更新:
@Adrian 的回答接近我正在寻找的内容并部分解决了问题。它returns以下
问题是 id 列不明确,我无法弄清楚如何最终得到这样一个干净的数组,理想情况下:
Array
(
[title] => some article
[id] => 1
[tags] => Array
(
[0] => Array
(
[id] => 1
[name] => php
)
[1] => Array
(
[id] => 2
[name] => jquery
)
)
[images] => Array
(
[0] => Array
(
[id] => 1
[fileName] => pic1.jpg
)
[1] => Array
(
[id] => 2
[fileName] => pic2.jpg
)
)
)
然后,结合 Daniel 和 Adrian 的回答,我得到了这个查询,returns 更具可读性(我猜)我可以处理以实现上述数组(我想迭代和删除重复项):
SELECT
a.id,
a.title,
a.dateCreated as articleDate,
i.id as imageId,
i.fileName as imageFile,
t.id as tagId,
t.name as tagName
FROM
articles a
JOIN
article_tags ta ON a.id = ta.articleId
JOIN
tags t ON ta.tagId = t.id
JOIN
images i ON a.id = i.articleId
WHERE
a.id = 1
这个returns:
性能方面,因为这个可能的解决方案 returns 许多行具有重复数据(取决于与文章关联的标签和图像的数量)。 这比我开始时进行的初始多次调用更好吗?
SELECT
a.id,
a.title,
a.dateCreated as articleDate,
i.fileName as imageFile,
t.name as tagName
FROM
articles a
JOIN
images i ON a.id = i.articleId
JOIN
tag_article ta ON a.id = ta.articleId
JOIN
tags t ON ta.articleId = t.id
WHERE
a.id = ?
试试这个代码:
编辑:
$sql = "SELECT
articles.id,
articles.title,
articles.dateCreated as articleDate,
article-images.id as imageId,
article-images.fileName as imageFile,
tags.id as tagId,
tags.name as tagName
FROM articles
JOIN tag-article ON articles.id = tag-article.articleId
JOIN tags ON tag-article.tagId = tags.id
JOIN article-images ON article-images.articleId = articles.id
WHERE articles.id=:id"
$query = $this->db->prepare($sql);
$parameters = array(':id' => $id);
$result = $query->execute($parameters);
return ($result) ? $query->fetch() : 0;
?>
我找到了一种方法来实现我想要的,方法是结合@Adrian 和@Daniel 的回答 post,他们为我指明了正确的方向(谢谢!)并添加了 CONCAT 的使用, GROUP_CONCAT 和 CAST(混合 int 和 varchar)。
我对这两个答案都投了赞成票,这部分解决了问题,但我认为我应该 post 我自己的答案,因为(我认为)是一个更清晰的解决方案,至少对于我想要的,它可能会有所帮助别人。
这是SQL
SELECT
a.id,
a.title,
GROUP_CONCAT( DISTINCT CAST( t.id AS CHAR( 50 ) ) , ':', t.name ORDER BY t.id SEPARATOR ',' ) AS 'tags',
GROUP_CONCAT( DISTINCT CAST( i.id AS CHAR( 50 ) ) , ':', i.fileName ORDER BY i.id SEPARATOR ',' ) AS 'images'
FROM
articles a
JOIN
article_tags ta ON a.id = ta.articleId
JOIN
images i ON a.id = i.articleId
JOIN
tags t ON ta.tagId = t.id
WHERE
a.id = :id
此查询 return 如下,id = 1:
包含文章数据、标签和图像的单行 id:value 格式,我认为在 PHP 中解析起来非常容易,甚至可以用于 return json 字符串,只需添加另一个 concat。