使用PHP / MySql中的单个查询从几个表中获取“相关”数据?

时间:2015-03-18 02:49:35

标签: php mysql sql database pdo

我正在从头开发一个小型PHP应用程序来练习/学习。目标是能够创建文章。每篇文章都可以有标签和图像,存储在自己的表格中。 由于文章可以包含许多标签,并且许多文章中都可以出现单个标签,因此我还创建了一个标签/文章'协会表。

现在,当我想编辑或显示文章时,我通​​过执行多个数据库请求来获取其数据(以及标签和图像)...例如,给定文章ID,我执行以下操作:

$article = getArticle($articleId);
$tags = getArticleTags($articleId);
$images = getArticleImages($articleId);

它有效,但我不确定它是否是一种干净的方法和良好的性能。 所以我想知道,可以通过使用单个查询来完成吗? 我试过但是对UNIONs和JOINs有些困惑。 如果没有,是否有更好/推荐的方法来实现这一目标?

我使用的(简化)表格是:

文章表:

┌-----┬------------------┬------------------------┐
│ id  │ title            │ dateCreated            │
├-----┼------------------┼------------------------┤
│ 1   │ some article     │ 2015-03-17 12:38:49    │
├-----┼------------------┼------------------------┤
│ 2   │ article two      │ 2015-03-17 15:00:24    │
└-----┴------------------┴------------------------┘ 

标签表:

┌-----┬------------------┬------------------------┐
│ id  │ name             │ dateCreated            │
├-----┼------------------┼------------------------┤
│ 1   │ php              │ 2015-03-17 15:01:05    │
├-----┼------------------┼------------------------┤
│ 2   │ jquery           │ 2015-03-17 15:24:12    │
└-----┴------------------┴------------------------┘ 

tag-article关联表:

┌-----┬--------┬-------------┬------------------------┐
│ 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    │
└-----┴--------┴-------------┴------------------------┘

article-images表:

┌-----┬-----------┬-------------┬------------------------┐
│ 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的回答接近于我正在寻找并部分解决问题的答案。它返回以下enter image description here

问题是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的答案,我最终得到了这个查询,它返回了一些更具可读性(我猜)我可以处理以实现上述数组(我想象迭代和删除重复项:)

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

返回:

enter image description here

性能方面,因为这种可能的解决方案会返回包含重复数据的许多行(取决于与文章关联的标记和图像的数量)。 这比我开始的初始多次通话更好吗?

3 个答案:

答案 0 :(得分:1)

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 = ?

答案 1 :(得分:1)

试试这段代码:

编辑:     

$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;

?>

答案 2 :(得分:1)

我找到了一种方法来实现我想要的,通过结合@Adrian和@Daniel发布的答案,他们指出了我正确的方向(谢谢!)并添加了CONCAT,GROUP_CONCAT和CAST(混合使用) int和varchar)。

我赞成了两个答案,这部分地解决了问题,但我认为我应该发布我自己的答案,因为(我认为)是一个更清洁的解决方案,至少对我想要的东西,它可能会帮助其他人。

这是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

此查询返回以下内容,id = 1:

enter image description here

包含文章数据,标签和id:value格式的图像的单行,我认为在PHP中解析非常容易,甚至可以通过添加另一个concat来返回json字符串。