你可以使用SELECT JOIN来解决问题及其标签吗?

Let's take SO site. It has questions and questions have its tags. My question is, what is the best way to display questions and its tags. Let's say they are in diffirent tables.

SELECT * FROM questions RIGHT JOIN tags ON questions.id = tags.question_id

But there are more tags than one, so how would we display them together? Or we just have to make 2 SELECTs? Because if yes, then it would take lots of resources, because we display more than 1 question in one page.

Firstly SO requires at least one tag so the right join isn't needed, it should actually be a left outer join if you want to do what I think you do...

As you say, there can be more than one tag, which suggests that we need an or or in or returning multiple rows or doing some sort of string aggregation.

However, that wouldn't be a very good data model as you still need to know what tags that question has. So, the best way to do it would be to store the tags in the posts table, without bothering with the tag_id mess.

This is what SO actually does.

Your query (would actually work) would then be:

select Tags
  from Posts
 where Id = ?

Tags is a nvarchar(150), which is a separated list of the tags for that question, in the same format you see them on your question if that's of interest!

SO will automatically create a tag whenever a new one is entered and uses synonyms to ensure that people's mis-spellings get corrected. So, there's a Tags table Id | TagName and a TagSynonoyms table, ( I don't know where the wiki summaries are kept ). You can then at the assignment of a tag check to see whether it's got a synonym and correct it before writing to the Posts table.

To answer your question though, the query would be ( also would actually work ):

select p.*, t.TagName
  from Posts p
  join PostTags pt
    on p.Id = pt.PostId
  join Tags t
    on pt.TagId = t.Id

I don't think that's what SO actually does - I don't even know why there is a PostTags or a Tags table as any tag is allowed there's no need to do the foreign key lookup to check whether a tag exists. And there's no need to store the relationship in this normalised way. You only need the column Tags in Posts and the TagSynonyms table to ensure that the synonyms system works. Then, if the primary key wasn't Id but TagName you only need one look-up.

Assuming tags table has a name column, I'd use GROUP_CONCAT function.

SELECT questions.*, 
       GROUP_CONCAT(DISTINCT tags.name -> ORDER BY tags.id SEPARATOR ',')  AS `tag_names`,
       GROUP_CONCAT(DISTINCT tags.id -> ORDER BY tags.id SEPARATOR ',')  AS `tag_ids`
FROM   questions 
       RIGHT JOIN tags 
         ON questions.id = tags.question_id 

This will show 2 column tag_names and tag_ids where values will be comma separated. When we fetch the values We can explode it and get the real values.

   while($row=mysql_fetch_assoc($result)){
       $tag_names = explode("," $row['tag_names'];
       $tag_ids = explode("," $row['tag_ids'];
       $tags = array_combine($tag_ids, $tag_names);
   }

Now $tags becomes a associated array where its key is the tag id and value is the tag name.

If you use following expression it'll create a JSON. But dont rely on it if your tag names have any characters which needs to be escaped (e.g. ")

Concat('{', GROUP_CONCAT(Concat('"', tags.id, '":"', tags.name, '"' ) 
               ORDER BY 
               tags.id), '}') AS `tag_o`

Get JSON object by $tags = json_decode($row['tag_o']);

The below one gives you all the tags of a specific question.

SELECT * FROM `questions` q INNER JOIN `tags` t ON q.id = t.question_id;

This assumes that your tags table is a many-to-many between tag and questions.

SELECT questions.*, GROUP_CONCAT(CONCAT(tag.id, '::', tag.name))
FROM questions
LEFT JOIN tags
    ON questions.id = tags.question_id
LEFT JOIN tag
    ON tags.tag_id = tag.id
GROUP BY questions.id