我怎样才能在Oracle中简化这个SQL查询?

时间:2011-01-04 23:13:35

标签: php sql oracle simplify

在搜索并阅读了一点之后,我为我的应用程序提出了以下SQL查询:

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字段的类型为DATEUserAge是计算该日期字段的年龄的函数。

表格结构如下:

[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查询来实现相同的结果。基本上我会添加一些像上面那些但具有不同条件的行,特别是在日期。

2 个答案:

答案 0 :(得分: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

可能会略微简化一下吗?

答案 1 :(得分: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;

总的来说,我可能只是保留你的查询。您拥有的查询很容易理解,并且可能表现得相当不错。