2011-01-04 2 views
0

После поиска и чтения немного я придумал следующий запрос SQL для моего приложения:Как я могу упростить этот SQL-запрос в Oracle?

SELECT 
    ROUND(AVG(CASE WHEN gender = 'M' THEN rating END), 1) avgAllM, 
    COUNT(CASE WHEN gender = 'M' THEN rating END) countAllM, 
    ROUND(AVG(CASE WHEN gender = 'F' THEN rating END), 1) avgAllF, 
    COUNT(CASE WHEN gender = 'F' THEN rating END) countAllF, 
    ROUND(AVG(CASE WHEN gender = 'M' AND UserAge(birth_date) <= 18 THEN rating END), 1) avgU18M, 
    COUNT(CASE WHEN gender = 'M' AND UserAge(birth_date) <= 18 THEN rating END) countU18M, 
    ROUND(AVG(CASE WHEN gender = 'F' AND UserAge(birth_date) <= 18 THEN rating END), 1) avgU18F, 
    COUNT(CASE WHEN gender = 'F' AND UserAge(birth_date) <= 18 THEN rating END) countU18F 
FROM movie_ratings mr INNER JOIN accounts a 
    ON mr.aid = a.aid 
WHERE mid = 5; 

И мне интересно, как я могу упростить это, если это возможно. Поле birth_date имеет тип DATE и UserAge - это функция для расчета возраста с этого поля даты.

В таблице структуры заключаются в следующем:

[ACCOUNTS] 
aid(PK), birth_date, gender 

[MOVIE_RATINGS] 
mid(PK), aid(PK,FK), rating 

Я ищу две вещи:

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

Например, что-то вроде этого:

$info[0]['avgAllM'] 
$info[0]['countAllM'] 
$info[1]['avgAllF'] 
$info[1]['countAllF'] 
$info[2]['avgU18M'] 
$info[2]['countU18M'] 
$info[3]['avgU18F'] 
$info[3]['countU18F'] 

Вместо:

$info['avgAllM'] 
$info['countAllM'] 
$info['avgAllF'] 
$info['countAllF'] 
$info['avgU18M'] 
$info['countU18M'] 
$info['avgU18F'] 
$info['countU18F'] 

Я даже не знаю, если это возможно, так что я на самом деле интересно, если это и как это можно сделать.

Почему я хочу все это? Ну, SQL-запрос выше - всего лишь фрагмент полного SQL, который мне нужно сделать. Я еще не сделал этого, потому что прежде чем выполнять всю работу, я хочу знать, есть ли более компактный SQL-запрос для достижения того же результата. В основном я добавлю еще несколько строк, например, выше, но с разными условиями, особенно на дату.

+0

Без дополнительной информации о структуре вашей таблицы Почему у вас есть предложение where, которое фильтрует для mid = 5? Чего вы действительно пытаетесь достичь? для вашего многомерного массива, не будет ли в группе sql help? – clyc

+0

Обновлен табличными структурами, хотя я не думаю, что они актуальны. Я не уверен, как я могу использовать GROUP BY для этого ... –

ответ

3

Вы можете создать VIEW со следующим определением

SELECT 
     CASE WHEN gender = 'M' THEN rating END AS AllM, 
     CASE WHEN gender = 'F' THEN rating END AS AllF, 
     CASE WHEN gender = 'M' AND UserAge(birth_date) <= 18 THEN rating END AS U18M, 
     CASE WHEN gender = 'F' AND UserAge(birth_date) <= 18 THEN rating END AS U18F 
     FROM movie_ratings mr INNER JOIN accounts a 
     ON mr.aid = a.aid 
     WHERE mid = 5 

Затем выберите из этого

SELECT ROUND(AVG(AllM), 1) avgAllM, 
     COUNT(AllM)   countAllM, 
     ROUND(AVG(AllF), 1) avg, 
     COUNT(AllF)   countAllF, 
     ROUND(AVG(U18M), 1) avgU18M, 
     COUNT(U18M)   countU18M, 
     ROUND(AVG(U18F), 1) avgU18F, 
     COUNT(U18F)   countU18F 
FROM yourview 

Может упростить вещи немного?

+0

Я мало знаю о представлениях, но считаю, что это будет невозможно, поскольку предложение WHERE зависит от переменной, это не всегда 'mid = 5', это был всего лишь пример. –

+0

Поняв немного больше о представлениях, я отредактировал тот, который вы опубликовали, и вставил поле «mid» в SELECT и переместил предложение WHERE на другой выбор, кажется, работает нормально. Я буду принимать это как правильный ответ, если кто-то не предоставит лучший способ или не будет иметь хороших аргументов против представлений в этом случае. –

+0

Как насчет части PHP, любой идеи? –

0

Это может быть случай оптимизации слишком рано. Запрос делает то, что вам нужно, и только реально выглядит сложным, потому что это так. Я не уверен, что обязательно будут любые трюки, которые помогут. Вероятно, это зависит от характеристик ваших данных. Является ли запрос медленным? Считаете ли вы, что это может быть быстрее?

Возможно, стоит перестроить его следующим образом. Поскольку все условия полагаются на таблицу ACCOUNTS, которую я предполагаю, будет значительно меньше таблицы MOVIE_RATINGS, вы можете выполнить все вычисления на меньшем наборе данных, что может быть быстрее. Хотя, если вы выбираете только один фильм за раз (mid = 5), то, вероятно, этого не будет.

Я не совсем уверен, что это сработает, но подумайте, что нужно.

SELECT 
    ROUND(AVG(rating * AllM), 1) avgAllM, 
    COUNT(rating * AllM) countAllM, 
    ROUND(AVG(rating * AllF), 1) avgAllF, 
    COUNT(rating * AllF) countAllF, 
    ROUND(AVG(rating * AllM * U18), 1) avgU18M, 
    COUNT(rating * AllM * U18) countU18M, 
    ROUND(AVG(rating * AllM * U18), 1) avgU18F, 
    COUNT(rating * AllM * U18) countU18F 
FROM 
    movie_ratings mr 
    INNER JOIN (
    select 
     aid, 
     case when gender = 'M' then 1 end as AllM, 
     case when gender = 'F' then 1 end as AllF, 
     case when UserAge(birth_date) <= 18 then 1 end as U18 
    from accounts) a ON mr.aid = a.aid 
WHERE mid = 5; 

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

+0

Любые причины не использования метода VIEW, предложенного Мартином? –

+0

BTW: Я никогда не предлагал использовать View для оптимизации. ОП спросил об упрощении. Насколько мне известно, он будет иметь нулевой эффект на производительность в любом случае. –

+0

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

Смежные вопросы