MySQL/PHP:在一个查询中使用多个子查询来选择多个结果,这是一个坏主意吗?


MySQL/PHP: Using multiple sub-queries in a query selecing multiple results, is it a bad idea?

对不起,如果标题有点...蹩脚。基本上,我正在编写一个小论坛,并使用多个子查询来选择论坛中的线程数,帖子数和最后一篇文章的日期,同时抓取论坛的信息以显示在主页上!

这是我的查询,因为我不善于解释事情:

SELECT `f`.*,
    (SELECT COUNT(`id`)
    FROM `forum_threads` 
    WHERE `forumId1` = `f`.`id1`
        AND `forumId2` = `f`.`id2`) AS `threadCount`,
    (SELECT COUNT(`id`)
    FROM `forum_posts` 
    WHERE `forumId1` = `f`.`id1`
        AND `forumId2` = `f`.`id2`) AS `postCount`,
    (SELECT `date`
    FROM `forum_posts` 
    WHERE `forumId1` = `f`.`id1` 
        AND `forumId2` = `f`.`id2` 
        ORDER BY `date` DESC LIMIT 1) AS `lastPostDate`
FROM `forum_forums` AS `f`
ORDER BY `f`.`position` ASC, `f`.`id1` ASC;

并且我使用通用的 foreach 循环来显示结果:

foreach($forums AS $forum) {
    echo $forum->name .'<br />';
    echo $forum->threadCount .'<br />';
    echo $forum->postCount .'<br />';
    echo $forum->lastPostDate .'<br />';
}

(当然不完全是这样,但为了解释...

现在我想知道这对性能是否"不利",或者是否有更好的方法?假设每个论坛中都有相当多的帖子和主题。

我最初在论坛表本身中存储"帖子","线程"和"lastPost"列,并且每次有人创建新主题或帖子时都会增加(帖子=帖子+ 1(值。虽然我也有这个想法,想知道它是否有任何好处。:P

我会做一些不同的事情:

在我看来,所有这三个字段:threadCountpostCountlastPostDate 都是您可以在单独的表上维护的字段,例如forum_stats,它只能容纳 4 列:
* forum_id
* thread_count
* post_count
* last_post_date

这些列可以通过以下方式更新。插入/更新时触发
如果您在更新操作期间支付这笔小开销 - 您将获得一个非常快的select查询(无论您拥有多少论坛/帖子/线程,它都会保持非常快(。

另一种方法(不是我们好的TMO(:
创建统计信息表并每天(或每隔几个小时(运行一个批处理作业,该作业将更新统计信息。代价是您显示的数据永远不会是最新的,并且作业可能需要资源,例如,您可能希望仅在晚上运行作业,因为它很重并且您不希望它影响大多数网站访问者。

通常,从性能的角度来看,这种事情很糟糕,最好使用可以从单行获取的计数器列。保持这些同步可能很烦人,但是一旦它们在那里,就没有检索成本。

您已经确定了要检索的数据,因此接下来需要做的是首先弄清楚如何将这些数据放在那里。 @alfasin的回答描述了一个示例架构,虽然将其放在单独的表中是一个想法,但将它们放在主表中通常不会有太多麻烦。如果您担心锁定,请分小批量更新。

一种方法是编写一个TRIGGER,用于在各种表中添加和删除记录时更新计数器。这往往会隐藏很多复杂性,如果逻辑经常变化并且人们需要了解系统的工作原理,这可能是一件坏事。

一个简单的方法是在创建或删除会更新列的内容后,使用其他查询摆弄列。例如,如果您在创建帖子时调整最后发布日期是微不足道的。

如果这些计数器变得有点糟糕,并且它们最终会搞砸,您需要一种方法来使它们恢复同步。一个简单的方法是编写一个VIEW,产生与查询现在相同的结果,也许重写以改用LEFT JOIN,然后在可能的情况下UPDATE反对。如果MySQL无法处理使用自身视图更新表,这可能涉及使用临时表,但这通常没什么大不了的。