drls2738 2015-03-18 02:49
浏览 48
已采纳

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

I'm developing a small PHP app from scratch to practice/learn. The goal is to be able to create articles. Each article can have tags and images, stored in their own tables. Since an article can have many tags and a single tag can be present in many articles, I also created a 'tag/article' association table.

Now, when I want to edit or display an article, I get its data (plus tags and images) by doing several DB requests... for example, given an article id, I do something like:

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

It works, but I'm not sure if it's a clean approach and good performance-wise. So I was wondering, can this be done by using a single query? I tried but got a bit confused with UNIONs and JOINs. If not, is there any better/recommended approach to achieve this?

The (simplified) tables I'm using are:

articles table:

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

tags table:

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

tag-article association 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    │
└-----┴--------┴-------------┴------------------------┘

article-images 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    │
└-----┴-----------┴-------------┴------------------------┘

Also, I'm using PDO this way, for example, to get an article:

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

Any input will be greatly appreciated. Thanks!


UPDATE:

@Adrian's answer is close to what I'm looking for and partially solves the question. It returns the following enter image description here

The thing is that the id columns are ambiguous and I can't figure out how to end up with a clean array like this, ideally:

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
                )

        )

)

Then, combining both Daniel's and Adrian's answers, I ended up with this query, which returns something more readable that (I guess) I could process to achieve the above mentioned array (I imagine that iterating and removing duplicates):

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

This returns:

enter image description here

performance-wise, since this possible solution returns many rows with duplicated data (depending on how many tags and images are associated with an article). Is this better than doing the initial multiple calls I started with?

  • 写回答

3条回答 默认 最新

  • duanao3204 2015-03-18 18:52
    关注

    I've found a way to achieve what I wanted, by combining the answers posted by @Adrian and @Daniel who pointed me in the right direction (thanks!) and adding the use of CONCAT, GROUP_CONCAT and CAST (to mix int and varchar).

    I upvoted both answers, which solved the problem partially, but I thought I should post my own answer since (I think) is a cleaner solution, at least for what I wanted, and it may help someone else.

    Here's the 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
    

    This query returns the following, with id = 1:

    enter image description here

    A single row containing the article's data, its tags and images in a id:value format, which I think it's very easy to parse in PHP and can even be used to return a json string by just adding another concat.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 CSS实现渐隐虚线框
  • ¥15 有没有帮写代码做实验仿真的
  • ¥30 vmware exsi重置后登不上
  • ¥15 易盾点选的cb参数怎么解啊
  • ¥15 MATLAB运行显示错误,如何解决?
  • ¥15 c++头文件不能识别CDialog
  • ¥15 Excel发现不可读取的内容
  • ¥15 关于#stm32#的问题:CANOpen的PDO同步传输问题
  • ¥20 yolov5自定义Prune报错,如何解决?