【问题标题】:Find the movies with the highest average rating using SQL max()使用 SQL max() 查找平均评分最高的电影
【发布时间】:2016-04-07 07:15:45
【问题描述】:

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

我看到了其他人提出的解决方案,例如,

  1. fetch the row with max values
  2. get top entries

但我希望在这里了解的是我目前的解决方案在哪里以及为什么出错了。


电影分级表:

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,stars代表评论者排名。

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

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

【问题讨论】:

  • 您应该在第二个查询中添加另一个 group by 子句,即 Select mID, max(avg_stars) AS Max_score From ( Select mID, avg(stars) AS avg_stars From Rating Group by mID) Group By mID
  • 这应该用通用标准 SQL 或 RDBMS 特定语法来回答吗?
  • 您似乎将 MySQL 用作 DBMS,它允许使用非标准语法:当您返回 mID 而不将其添加到 GROUP BY 时,MySQL 返回具有最大值(平均值)和随机 mID 值。
  • 您好,dnoeth,您的解释听起来很合理。但添加 Group By 似乎不起作用。我的 DBMS 是 SQLite。您的解决方案是否适用于 MySQL 但不适用于 SQLite?

标签: sql sqlite


【解决方案1】:

您似乎将 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...

【讨论】:

  • 该问题具体要求使用SQLite。请参阅下面的WITH 答案。 SQLite 是否支持max(avg( )) 语法?
  • 嗨@dnoeth,“当您返回 mID 而不将其添加到 GROUP BY MySQL 时,返回具有最大值(平均值)的单行和 mID 的随机值。” SQLite会发生这种情况吗?你能提供更多这样的例子吗?
  • @user3580870:sqlite 标签是在我写完答案后添加的,开头只是sql
  • @enaJ:我不知道 SQLite,在这种情况下它可能类似于 MySQL。
【解决方案2】:

也许 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 子句可以轻松提供进一步曲折的路径。

【讨论】:

  • 这是我将用于:效率、教育或可读性目的。除非你的 SQLite 是旧版本。
【解决方案3】:

这只是@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

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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

    • 这适用于当前表。如果有多个具有最高价值的对象,它也会起作用吗?
    • @enaJ:不,不会。
    • @enaJ,如果前 2 部电影的平均评分相同,并且如果您想获得前 2 部电影,您是否还会根据您的要求考虑另一部电影(第 3 条记录)?
    • @Rajeev,我会考虑所有得分最高的电影,可能有 2 部、3 部或 4 部得分相同的电影
    猜你喜欢
    • 2021-09-27
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 2017-07-20
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多