怎么做这个复杂的查询?

时间:2013-05-17 08:03:48

标签: mysql sql database

这是我的数据库架构:

Post:
id
title
body
date

Tag:
id
title

Post_Tag:
id
id_post
id_tag

Comment:
id
id_post
body
date

帖子和标签之间有很多关系。

我需要在主页上打印最新的10个帖子:

<a href="post.php?id=ID_POST">POST_TITLE</a>

POST_BODY

<a href="tag.php?id=ID_TAG_1"> TAG_TITLE_1 </a>
<a href="tag.php?id=ID_TAG_2"> TAG_TITLE_2 </a>
<a href="tag.php?id=ID_TAG_3"> TAG_TITLE_3 </a>

COMMENTS_NUMBER

最好的查询是什么?

我试过这个,但是效果不好因为每个帖子都有多行:

SELECT p.title, p.id, p.date, t.title, t.id, COUNT(c.id)
             FROM post p
             LEFT JOIN post_tag pt 
             ON p.id=pt.id_post
             LEFT JOIN tag t 
             ON t.id=pt.id_tag
             LEFT JOIN comment c
             ON p.id=c.id_post
             GROUP BY p.title, p.id, p.date, t.title
             ORDER BY p.date DESC

2 个答案:

答案 0 :(得分:3)

您可能不希望将此作为单个查询执行,但理论上您可以这样做。

SELECT
    Post.id AS post_id,
    Post.title AS post_title,
    Post.body AS post_body,
    GROUP_CONCAT(CONCAT(Tag.id, "|", Tag.title) SEPARATOR '#') AS tags,
    COUNT(Comment.id) AS comment_count
FROM Post
LEFT JOIN Comment ON Post.id = Comment.id_post
LEFT JOIN Post_Tag ON Post.id = Post_Tag.id_post
LEFT JOIN Tag ON Tag.id = Post_Tag.id_tag
GROUP BY Post.id
ORDER BY Post.date ASC

我没有检查过这个,因为我无法访问您的数据,但它应该可以正常工作。您需要手动拆分标签,这些标签将以“ID | TITLE#ID | TITLE”的格式显示,但这是唯一需要的额外处理。

或者,您可以通过在两个单独的查询之间拆分此工作负载来避免标记的GROUP_CONCAT:

SELECT
    Post.id AS post_id,
    Post.title AS post_title,
    Post.body AS post_body,
    COUNT(Comment.id) AS comment_count
FROM Post
LEFT JOIN Comment ON Post.id = Comment.id_post
GROUP BY Post.id
ORDER BY Post.date ASC

从这里,您将存储所有单独的post_id,并在第二个查询中使用它们,如下所示:

SELECT
    Tag.id,
    Tag.title
FROM Post_Tag
INNER JOIN Tag ON Post_Tag.id_tag = Tag.id
WHERE Post_Tag.id_post IN (**comma-separated list of Post IDs**)

这意味着您可以执行两个查询,否则您必须执行一个查询以获取所有帖子,然后另一个查找标签的每个帖子 - 这是一个N + 1查询,而我上面提出的是解决这个问题的常用方法。

答案 1 :(得分:1)

如果我理解正确,那么您正试图制作一个“带标签和评论的帖子”系统,其关系如下: relationships


一个用于统治所有

的查询

根据Stephen Orr's answer的建议,您应该为代码执行GROUP_CONCAT以避免重复发帖。你还需要做一些小的后期处理来格式化标签,除非你只是想要标签标题,但那就是它。

这是一个例子

SELECT
    p.ID,
    p.title,
    p.body,
    p.c_date,
    GROUP_CONCAT(DISTINCT CONCAT_WS('|', CAST(t.ID AS CHAR), t.title) SEPARATOR ';') AS tags,
    COUNT(c.ID) AS comments
FROM Post p
    LEFT JOIN Comment c ON p.ID = c.id_post
    LEFT JOIN Post_Tag pt ON p.ID = pt.id_post
    LEFT JOIN Tag t ON pt.id_tag = t.ID
GROUP BY p.ID, p.title, p.body, p.c_date
ORDER BY p.c_date DESC

请注意,我在标记ID上使用显式类型转换。

以下是对GROUP_CONCATCONCAT_WS的一些引用。