开发者

MySQL sub-queries, views, and procedures; which (if any) are correct?

开发者 https://www.devze.com 2023-03-31 15:27 出处:网络
I\'ve written a query, that contains a set of sub-queries: SELECT `board_id`, `post_count`, ROUND(`post_age_avg`, 3) as `post_age_avg`,

I've written a query, that contains a set of sub-queries:

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_rat开发者_开发百科ing_avg`,
        TIME_TO_SEC(TIMEDIFF(NOW(), `board`.`created_on`)) / 3600 AS `board_age`
    FROM `board`) AS `board_stats`

Now, while I'm more than open to suggestions on optimizing, or otherwise improving this query, my question pertains to in what way I could store this query for future use.

A view would be great, however as per the MySQL manual page on CREATE VIEW:

  • The SELECT statement cannot contain a sub-query in the FROM clause.

So I've encapsulated the query in a procedure. Works fine, calling:

CALL get_board_stats();

However I quickly discovered that there is limited (read zero) flexibility using procedures, with respect to using the results as a sub-query. Like others on SO with similar questions, I found:

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

And any permutation thereof, is syntactically invalid.

So my question is; how can I go about achieving (if at all possible) a scenario where this query can be stored for later use as a "virtual table" in subsequent queries, allowing one to do something like:

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

Alrighty @OMG Ponies, this is the final version I'm going with (for now), as it produces an identical result-set, with the same numerical precision. Its quite similar to yours, except I omitted the JOIN in favor of (once again) sub-queries, though this time not as columns in a derived table. Something tells me this is less efficient though (in comparison for example to the JOIN solution) perhaps you can shed some light on that:

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`

(I'd post something better formatted than this wad, but Workbench beautify sucks with larger queries, and I don't have the effort :P)

For some reason your solution continued to provide incorrect results for board_usage and board_rating.


You don't need the derived table - Use:

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

...though the use of ROUND I don't recommend until after whatever calculation you need the values for.

0

精彩评论

暂无评论...
验证码 换一张
取 消