在mysql中需要有关多表查询的帮助

时间:2009-09-20 16:25:47

标签: php sql mysql

我正在与kohana建立一个论坛。我知道那里已经有好的,免费的论坛软件了,但这是一个家庭网站,所以我想我会用它作为学习经验。我也没有使用Kohana内置的ORM,因为我想在构建论坛的过程中更多地了解SQL。

对于我的论坛,我有4个主要表格:

  • USERS
  • TOPICS
  • POSTS
  • COMMENTS

TOPICS 表:id(自动递增),主题行。

USERS 表:用户名,电子邮件,名字和姓氏以及其他一些不相关的行

POSTS 表:id(自动递增),post-title,post-body,topic-id,user-id,post-date,updated-date,updated-by(将包含发表最新评论的人的用户ID

评论表:ID(自动递增),post-id,用户ID和评论


在我想要的主论坛页面上:

  • 所有主题的列表
  • 每个主题的帖子数
  • 上次更新的帖子,以及更新后的内容
  • 最近更新的主题,最有可能是“ORDER BY updated-date”

这是我到目前为止的查询:

SELECT topics.id AS topic-id, 
       topics.topic, 
       post-user.id AS user-id, 
       CONCAT_WS(' ', post-user.first-name, post-user.last-name) AS name, 
       recent-post.id AS post-id, 
       post-num.post-total, 
       recent-post.title AS post-title, 
       recent-post.update_date AS updated-date, 
       recent-post.updated-by AS updated-by
  FROM topics
  JOIN (SELECT posts.topic-id,
               COUNT(*) AS post-total                 
          FROM POSTS
         WHERE posts.topic-id = topic-id 
      GROUP BY posts.topic-id) AS post-num ON topics.id = post-num.topic-id
  JOIN (SELECT posts.* 
          FROM posts 
      ORDER BY posts.update-date DESC) AS recent-post ON topics.id = recent-post.topic-id 
  JOIN  (SELECT users.*, 
                posts.user-id 
           FROM users, posts 
          WHERE posts.user-id = users.id) as post-user ON recent-post.user_id = post-user.id 
GROUP BY topics.id

此查询几乎可以正常运行,因为它将获取有帖子的主题的所有信息。 但它没有返回没有任何帖子的主题

我确信查询效率低且错误,因为它对posts表进行了两次子选择,但这是我能够达到目标的唯一方法。

4 个答案:

答案 0 :(得分:1)

  • Dash不是SQL标识符中的有效字符,但您可以改为使用“_”。
  • 您不一定要从单个SQL查询中获取所有。实际上,尝试这样做会使代码更难编写,有时也会使SQL优化器更难执行。
  • 在子查询中使用ORDER BY是没有意义的。
  • 将主键列topic_iduser_id等命名为(而不是每个表中的“id”),并且您不必在选择中对它们进行别名-list。

以下是我将如何解决这个问题:

首先获得每个主题的最新帖子,以及相关的用户信息:

SELECT t.topic_id, t.topic,
  u.user_id, CONCAT_WS(' ', u.first_name, u.last_name) AS full_name,
  p.post_id, p.title, p.update_date, p.updated_by
FROM topics t
INNER JOIN 
  (posts p INNER JOIN users u ON (p.updated_by = u.user_id))
  ON (t.topic_id = p.topic_id)
LEFT OUTER JOIN posts p2
  ON (p.topic_id = p2.topic_id AND p.update_date < p2.update_date)
WHERE p2.post_id IS NULL;

然后在单独的,更简单的查询中获取每个主题的帖子数。

SELECT t.topic_id, COUNT(*) AS post_total
FROM topics t LEFT OUTER JOIN posts p USING (topic_id)
GROUP BY t.topic_id;

合并应用程序中的两个数据集。

答案 1 :(得分:0)

为了确保您获得没有帖子的主题的结果,您需要使用LEFT JOIN而不是JOIN来进行主题和下一个表之间的第一次连接。 LEFT JOIN表示“总是为左表中的每一行返回一个结果集行,即使与右表没有匹配。”

现在要走了,但我稍后会尝试查看效率问题。

答案 2 :(得分:0)

我在子查询中使用left join来撤回正确的主题,然后你可以在其外做一些小工作来获取一些用户信息。

select
    s.topic_id,
    s.topic,
    u.user_id as last_updated_by_id,
    u.user_name as last_updated_by,
    s.last_post,
    s.post_count
from
    (
        select
            t.id as topic_id,
            t.topic,
            t.user_id as orig_poster,
            max(coalesce(p.post_date, t.post_date)) as last_post,
            count(*) as post_count --would be p.post_id if you don't want to count the topic
        from
            topics t
            left join posts p on
                t.id = p.topic_id
        group by
            t.topic_id,
            t.topic,
            t.user_id
    ) s
    left join posts p on
        s.topic_id = p.topic_id
        and s.last_post = p.post_date
        and s.post_count > 1 --0 if you're using p.post_id up top
    inner join users u on
        u.id = coalesce(p.user_id, s.orig_poster)
order by 
    s.last_post desc

此查询确实介绍了coalesceleft join,它们是非常好的概念。对于两个参数(如此处使用的那样),您也可以在MySQL中使用ifnull,因为它在功能上是等效的。

请记住,这是MySQL独有的(如果您需要移植此代码)。其他数据库具有其他功能(SQL Server中为isnull,Oracle中为nvl等,等等)。我使用coalesce以便我可以将此查询保持为ANSI-fied。

答案 3 :(得分:0)

这是一个非常复杂的查询。您应该注意,JOIN语句会将您的主题限制为有帖子的主题。如果主题没有帖子,则JOIN语句会将其过滤掉。

尝试以下查询。

SELECT * 
FROM
(
  SELECT T.Topic, 
         COUNT(AllTopicPosts.ID) NumberOfPosts, 
         MAX(IFNULL(MostRecentPost.Post-Title, '') MostRecentPostTitle,
         MAX(IFNULL(MostRecentPostUser.UserName, '') MostRecentPostUser
         MAX(IFNULL(MostRecentPost.Updated_Date, '') MostRecentPostDate
  FROM TOPICS
  LEFT JOIN POSTS AllTopicPosts ON AllTopicPosts.Topic_Id = TOPICS.ID
  LEFT JOIN 
     (
       SELECT * 
       FROM Posts P
       WHERE P.Topic_id = TOPICS.id
       ORDER BY P.Updated_Date DESC
       LIMIT 1
     ) MostRecentPost ON MostRecentPost.Topic_Id = TOPICS.ID
  LEFT JOIN USERS MostRecentPostUser ON MostRecentPostUser.ID = MostRecentPost.User_Id
  GROUP BY T.Topic
)
ORDER BY MostRecentPostDate DESC