MySQL sub-queries, views, and procedures; which (if any) are correct?
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 theFROM
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.
精彩评论