使用SQL max()

时间:2016-01-03 15:32:28

标签: sql sqlite

这是斯坦福在线数据库课程练习中的一个问题。 找到平均评分最高的电影。返回这些电影标题及其平均评分。使用 SQLite

我见过其他人友好建议的解决方案,例如,

  1. fetch the row with max values
  2. get top entries
  3. 但我希望在此理解的是,我现有的解决方案出现在何处以及为何出错。

    电影评级表:

    rID mID stars   ratingDate
    201 101 2   2011-01-22
    201 101 4   2011-01-27
    203 103 2   2011-01-20
    203 108 4   2011-01-12
    203 108 2   2011-01-30
    204 101 3   2011-01-09
    205 103 3   2011-01-27
    205 104 2   2011-01-22
    ...
    

    注意:mID表示电影ID,rID表示评论者ID,星号表示评论者排名的分数。

    我的第一个想法是使用以下代码获取每部电影的平均分数:

    Select mID, avg(stars) AS avg_stars
    From Rating
    Group by mID
    

    生成的摘要表是

    mID avg_stars
    101 3.0
    103 2.5
    104 2.5
    106 4.5
    107 4.0
    108 3.3
    

    然后我想选择得分列的最大值和相关的mID

    Select mID, max(avg_stars) AS Max_score
    From (
    Select mID, avg(stars) AS avg_stars
    From Rating
    Group by mID) T
    

    我希望得到:

    mID Max_score
    106 4.5
    

    但相反,我得到了:

    mID Max_score
    108 4.5
    

4 个答案:

答案 0 :(得分:2)

而不是子查询尝试使用order by并限制第一个结果:

SELECT mID, AVG(stars) AS avg_stars
FROM Rating
GROUP BY mID
ORDER BY avg_stars DESC LIMIT 1;

答案 1 :(得分:2)

您似乎使用MySQL作为DBMS,它允许使用非标准语法:

当您返回mID而不将其添加到GROUP BY时,MySQL会返回一行,其中包含最大(平均)和mID的随机值。

这是一个通用的标准SQL解决方案:

Select mID, avg(stars) AS avg_stars
From Rating
Group by mID
having avg(stars) =
 ( select max(avg_stars) -- find the max of the averages
   from 
     ( Select mID, avg(stars) AS avg_stars
       From Rating
       Group by mID
     ) T
 )

这可能是非常低效的,这就是为什么有几个专有语法扩展的原因。大多数DBMS(但不支持MySQL)支持标准SQL的窗口化聚合函数:

select *
from
 ( 
   Select mID, avg(stars) AS avg_stars,
      max(avg(stars)) as max_avg
   From Rating
   Group by mID
 ) T
where avg_stars = max_avg

编辑:

当您将SQLite添加为DBMS时,我的第二个查询将无效(SQLite也不支持分析函数)。

但是,由于支持WITH,您可以将#1简化为类似于@ user3580870的查询:

with cte as 
 ( Select mID, avg(stars) AS avg_stars
   From Rating
   Group by mID
 )
select * from cte
where avg_stars =
 ( select max(avg_stars) -- find the max of the averages
   from cte
 );

这仍然符合标准SQL ......

答案 2 :(得分:1)

也许一个WITH子句可以做到这一点。目前无法访问要测试的实时数据库,但查询应如下所示:

WITH sq AS 
  (SELECT mID, avg(stars) AS avg_stars FROM rating GROUP BY mID)
SELECT mId,avg_stars FROM sq t1 JOIN 
  (SELECT max(avg_stars) AS max_avg FROM sq) t2 
ON t1.avg_stars = t2.max_avg;

SQLite支持3.8.3版的WITH子句。该代码实际上是在问题中的数据上测试的。它还使计算明显比其他解决方案更有效(尽管它们可能通过一些智能查询分析进行优化)。此外,它是最清晰的,WITH条款可以轻松提供进一步扭曲的路径。

答案 3 :(得分:1)

这只是@SMA's答案的延伸。

如果要将重复记录视为单个实体,可以使用以下查询

SELECT mID,AVG(stars) 
FROM Rating
WHERE AVG(stars) IN (
            (SELECT AVG(stars)
             FROM Rating
             GROUP BY mID
             ORDER BY avg_stars DESC LIMIT 1)
            )
GROUP BY mID

上述查询可能不是优化查询。