Подзапросы, представления и процедуры MySQL;какие (если таковые имеются) являются правильными? - PullRequest
1 голос
/ 29 августа 2011

Я написал запрос, который содержит набор подзапросов:

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`

Теперь, когда я более чем открыт для предложений по оптимизации или улучшению этого запроса, мой вопросотносится к тому, каким образом я мог бы сохранить этот запрос для будущего использования.

A view было бы здорово, однако согласно странице справочника MySQL по CREATE VIEW:

  • Оператор SELECT не может содержать подзапрос в предложении FROM.

Итак, яинкапсулировал запрос в процедуру .Работает нормально, вызывая:

CALL get_board_stats();

Однако я быстро обнаружил, что существует ограниченная ( read zero ) гибкость использования процедур в отношении использования результатов в качестве подзапроса.Как и другие на SO с подобными вопросами, я обнаружил:

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

И любая их перестановка синтаксически неверна.

Так что мой вопрос таков;как я могу добиться (, если это вообще возможно ) сценария, в котором этот запрос может быть сохранен для последующего использования в качестве «виртуальной таблицы» в последующих запросах, позволяя сделать что-то вроде:

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

Ладно, @OMG Пони, это последняя версия, с которой я собираюсь ( на данный момент ), так как она производит идентичный набор результатов с тем же числовым значениемточность.Он очень похож на ваш, за исключением того, что я опустил 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_usage и board_rating.

1 Ответ

1 голос
/ 29 августа 2011

Вам не нужна производная таблица - Используйте:

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 Я не рекомендую, пока после какого-либо вычисления вам не понадобятся значения для.

...