Php 需要mysql中多表查询的帮助吗

Php 需要mysql中多表查询的帮助吗,php,sql,mysql,Php,Sql,Mysql,我正在与科哈纳建立一个论坛。我知道已经有好的,免费的,论坛软件了,但它是为一个家庭网站,所以我想我会用它作为一种学习经验。我也没有使用内置在Kohana中的ORM,因为我想在构建论坛的过程中了解更多关于SQL的信息 对于我的论坛,我有4个主要表格: 用户 主题 POSTS 注释 主题表格:id(自动递增),主题行 用户表格:用户名、电子邮件、名字和姓氏以及其他一些不相关的行 帖子表格:id(自动递增)、帖子标题、帖子正文、主题id、用户id、帖子日期、更新日期、更新人(其中将包含发表最新评论

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

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

  • 用户
  • 主题
  • POSTS
  • 注释
主题表格:id(自动递增),主题行

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

帖子表格:id(自动递增)、帖子标题、帖子正文、主题id、用户id、帖子日期、更新日期、更新人(其中将包含发表最新评论的人的用户id)

评论表格:id(自动递增)、帖子id、用户id和评论


在主论坛页面上,我希望:

  • 所有主题的列表
  • 每个主题的帖子数量
  • 上次更新的帖子,是谁更新的
  • 最新更新的主题位于顶部,很可能是“按更新日期订购”
以下是我到目前为止提出的问题:

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表进行了两次子选择,但这是我达到目的的唯一方法。

为了确保没有posts的主题得到结果,在主题和下一个表之间的第一次连接中,需要使用LEFT JOIN而不是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
这个查询确实引入了和,它们是非常好的概念。对于两个参数(就像这里使用的),您也可以在MySQL中使用,因为它在功能上是等效的


请记住,这是MySQL独有的(如果需要移植此代码)。其他数据库也有相应的功能(SQL Server中的
isnull
,Oracle中的
nvl
,等等)。我使用了
coalesce
,这样我就可以对这个查询进行所有的ANSI验证。

这是一个非常复杂的查询。您应该注意,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
  • 破折号在SQL标识符中不是有效字符,但可以改为使用“\u1”
  • 您不必从一个SQL查询中获取所有信息。事实上,这样做会使编写代码更加困难,有时还会使SQL优化器执行起来更加困难
  • 在子查询中使用
    orderby
    是没有意义的
  • 将主键列命名为
    topic\u id
    user\u id
    ,依此类推(而不是每个表中的“
    id
    ”),这样您就不必在选择列表中对它们进行别名
下面是我将如何解决这个问题: 首先获取每个主题的最新帖子,以及相关的用户信息:

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;

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

我现在明白了为什么格式化SQL查询同样重要了well@rexem-谢谢你帮我整理,这是我的第一篇SO帖子,我不确定我可以在其中添加多少html标记。你为什么要对标题和用户进行
max
?如果你有一个主题“ZOMG!Ponies!”和一个“ABC123”,那么无论“ABC123”是否是最后更新的主题,“ZOMG!Ponies!”都会返回…你可能是对的。我只按主题分组,我知道在其他系统中,如果不在GROUP by子句中包含列或在SELECT语句中聚合列,则SELECT将失败。我知道MySQL在这方面的工作方式有所不同,但就我个人而言,我无法编写一些我知道在其他系统中不起作用的东西。MySQL允许您将
内部连接
放入
内部连接
?谁写的数据库,Xzibit?虽然,这是一个相当巧妙的技巧。是的,括号连接实际上是标准SQL,而不仅仅是MySQL-ism。当您想要外部连接到一对与内部连接相关的表时,这种技术尤其有用,即
X左外部连接(Y内部连接Z)
i'll be,就是这样(我对此持怀疑态度,所以在本地SQL Server实例上尝试了它)。这相当于从内部联接中选择*(从b.id=c.id上的b内部联接中选择*)在a.id=b.id上,对吗?显然,这与没有参数时是一样的,但我假设对于外部连接行为,如果不小心的话,它会给你一些相当粗糙的结果。是的,它会产生类似的结果,但不使用子查询。圆括号是圆括号,你可以用它们来控制求值顺序,就像在一个算术表达式中一样。是的,如果你不小心的话,你可以通过外部连接得到粗糙的结果,但这总是正确的!:-)工作得很有魅力!谢谢这个网站将是非常小的规模,因为我说过它只为我的家人。我们只有大约30人,我怀疑我会因为这个问题而减速。但你能建议的任何其他清理都会很棒,谢谢
SELECT t.topic_id, COUNT(*) AS post_total
FROM topics t LEFT OUTER JOIN posts p USING (topic_id)
GROUP BY t.topic_id;