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条)

报告相同问题?

悬赏问题

  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘