MySQL子查询,视图和过程;哪个(如果有的话)是正确的?

时间:2011-08-29 03:24:54

标签: mysql sql views

我编写了一个查询,其中包含一组子查询:

SELECT
    `board_id`,
    `post_count`,
    ROUND(`post_age_avg`, 3) as `post_age_avg`,
    ROUND(`post_rating_avg`, 3) as `post_rating_avg`,
    ROUND(`board_age`, 3) as `board_age`,
    ROUND(1 - (`post_age_avg` / `board_age`), 3) AS `board_usage`,
    ROUND((`post_count` / `board_age`) * `post_rating_avg`, 3) AS `board_rating`
FROM
    (SELECT
        `board_id`,
        (SELECT COUNT(*) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`) AS `post_count`,
        (SELECT AVG(TIME_TO_SEC(TIMEDIFF(NOW(), `post`.`created_on`)) / 3600) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`) as `post_age_avg`,
        (SELECT AVG(`rating`) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`) AS `post_rating_avg`,
        TIME_TO_SEC(TIMEDIFF(NOW(), `board`.`created_on`)) / 3600 AS `board_age`
    FROM `board`) AS `board_stats`

现在,虽然我对优化或改进此查询的建议持开放态度,但我的问题是以何种方式存储此查询以供将来使用。

视图会很棒,但是根据CREATE VIEW上的MySQL手册页:

  
      
  • SELECT语句不能在FROM子句中包含子查询。
  •   

所以我在程序中封装了查询。工作正常,致电:

CALL get_board_stats();

然而,我很快发现,使用过程作为子查询时,使用过程的灵活性有限(读零)。像其他有类似问题的SO一样,我发现:

SELECT * FROM (CALL get_board_stats()) AS `board_stats`;

其中的任何排列在语法上都是无效的。

所以我的问题是;我怎样才能实现(,如果可能的话)一个场景,在这个场景中可以存储这个查询,以便以后在后续查询中用作“虚拟表”,允许人们做类似的事情:

SELECT * FROM /* give_me_board_stats_somehow() */ WHERE ...

Alrighty @OMG Ponies,这是我要使用的最终版本( for now ),因为它生成相同的结果集,具有相同的数值精度。它与你的非常相似,除了我省略了JOIN支持(再次)子查询,尽管这次不是派生表中的列。有些东西告诉我这样效率较低(比较JOIN解决方案)或许你可以对此有所了解:

SELECT
    `board`.`board_id`,
    (SELECT COUNT(*) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`) AS `post_count`,
    ROUND((SELECT AVG(TIME_TO_SEC(TIMEDIFF(NOW(), `post`.`created_on`)) / 3600) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`), 3) AS `post_age_avg`,
    ROUND((SELECT AVG(`rating`) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`), 3) AS `post_rating_avg`,
    ROUND(TIME_TO_SEC(TIMEDIFF(NOW(), `board`.`created_on`)) / 3600, 3) AS `board_age`,
    ROUND(1 - ((SELECT AVG(TIME_TO_SEC(TIMEDIFF(NOW(), `post`.`created_on`)) / 3600) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`) / (TIME_TO_SEC(TIMEDIFF(NOW(), `board`.`created_on`)) / 3600)), 3) AS `board_usage`,
    ROUND(((SELECT COUNT(*) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`) / (TIME_TO_SEC(TIMEDIFF(NOW(), `board`.`created_on`)) / 3600)) * (SELECT AVG(`rating`) FROM `post` WHERE `post`.`board_id` = `board`.`board_id`), 3) AS `board_rating`
FROM `board`

我发布的格式比这个格式更好,但Workbench美化很糟糕,查询较多,我没有努力:P

出于某种原因,您的解决方案继续为board_usageboard_rating提供不正确的结果。

1 个答案:

答案 0 :(得分:1)

您不需要派生表 - 使用:

CREATE VIEW your_view AS
   SELECT b.board_id
          COUNT(p.post_id) AS post_count,
          ROUND(AVG(TIME_TO_SEC(TIMEDIFF(NOW(), p.created_on)) / 3600), 3) AS post_age_avg,
          ROUND(AVG(p.rating), 3) AS post_rating_avg,
          ROUND(TIME_TO_SEC(TIMEDIFF(NOW(), b.created_on)) / 3600, 3) AS board_age,
          ROUND(1 - (AVG(TIME_TO_SEC(TIMEDIFF(NOW(), p.created_on)) / 3600) / TIME_TO_SEC(TIMEDIFF(NOW(), b.created_on)) / 3600), 3) AS board_usage,
          ROUND(COUNT(p.*) / (TIME_TO_SEC(TIMEDIFF(NOW(), b.created_on)) / 3600), 3) AS board_rating
     FROM BOARD b
LEFT JOIN POST p ON p.board_id = b.board_id
 GROUP BY b.board_id

......虽然使用了ROUND我不推荐,直到经过任何计算后你都需要这些值。