LeetCode 1341. 电影评分
题目描述




题意分析
查询两类结果:第一行是评价过最多电影的用户名,统计范围是全部评分记录;第二行是 2020 年 2 月平均评分最高的电影名,只统计该月评分。各自的指标并列时,选择名称字典序最小的一项。
Users提供用户名称,Movies提供电影名称,MovieRating提供用户、电影、评分和日期。评分表以电影与用户的组合作为主键,因此同一用户对同一电影只有一条记录,按用户统计评分行数就等于评价电影数量。输出列名为results,用户在前、电影在后。
解法:SQL 查询建模
核心思路
[!blue]
两个问题的分组对象、聚合指标和时间范围都不同,应分别求出各自的一行结果,再合并。不能先对所有评分统一筛月份,否则用户评价数量也会被错误限制在二月。
用户分支连接
MovieRating与Users,按用户编号和名称分组,用COUNT(*)统计电影数量。按数量降序、名称升序排序后取第一行,便同时完成最大值选择与并列时的字典序规则。电影分支连接
MovieRating与Movies,先筛选日期,再按电影编号和片名分组求AVG(rating)。日期范围使用左闭右开区间[2020-02-01, 2020-03-01),既保留二月二十九日,也排除三月一日;然后按均分降序、片名升序取第一行。两次连接都通过实体主键取名称,不会把一条评分复制成多条,聚合粒度仍分别是一位用户和一部电影。名称作为并列排序键只在指标相等时决定先后,不能先按名称选行再聚合。
让两个子查询各自完成排序与
LIMIT 1,外面加上顺序编号一和二,用UNION ALL保留两类结果。最外层再按编号排序、只输出results,显式保证行顺序,不依赖两个子查询写在 SQL 中的先后。
解题步骤
- 用户子查询连接用户名,按全部评分记录计数,按次数降序、姓名升序取一行。
- 电影子查询先筛出 2020 年二月评分,再计算每部电影平均分,按均分降序、片名升序取一行。
- 两支结果分别附加
sort_order = 1和2,用UNION ALL拼接。- 最外层按
sort_order排序,只返回results列。
代码实现
SELECT results
FROM (
SELECT top_user.results, 1 AS sort_order
FROM (
SELECT u.name AS results
FROM MovieRating AS r
JOIN Users AS u ON u.user_id = r.user_id
GROUP BY r.user_id, u.name
-- 先按全部评分数量选用户,并列时按名称选最小。
ORDER BY COUNT(*) DESC, u.name ASC
LIMIT 1
) AS top_user
UNION ALL
SELECT top_movie.results, 2 AS sort_order
FROM (
SELECT m.title AS results
FROM MovieRating AS r
JOIN Movies AS m ON m.movie_id = r.movie_id
-- 先限定二月记录,再参与后面的电影平均分计算。
WHERE r.created_at >= '2020-02-01'
AND r.created_at < '2020-03-01'
GROUP BY r.movie_id, m.title
-- 电影按平均分排序,不按评分总和。
ORDER BY AVG(r.rating) DESC, m.title ASC
LIMIT 1
) AS top_movie
) AS ranked
-- 显式保证用户结果在前、电影结果在后。
ORDER BY sort_order;
复杂度分析
设
R为评分记录数,G为两个分支产生的分组总数。SQL 的实际复杂度取决于索引、连接方式和执行计划。
- 在线性连接与聚合的计划下,处理评分记录为 $O(R)$,对分组结果完整排序为 $O(G\log(G+1))$;执行器也可能使用取首行优化。
- 聚合与分组排序通常需要 $O(G)$ 级中间状态,具体内存及临时存储还取决于执行器。
关键点总结
[!green]
- 用户按所有评分次数排名,电影仅按指定月份的平均分排名,统计范围不能混用。
- 主键保证一位用户对同一电影只评一次,因此
COUNT(*)对应评价电影数。- 两个子查询各自先完成双排序键和取首行,再合并结果。
- 排名用于选择哪一行,外层顺序编号用于安排两类结果,职责不同。
易错点总结
[!yellow]
- 电影使用评分总和会偏向评分记录多的电影,应比较平均分。
- 先算全年平均再筛月份,月份外的评分已经混入,无法得到二月均分。
- 把日期条件也用于用户分支,会少统计其他月份评价过的电影。
- 缺少名称升序排序键,指标并列时无法保证选到字典序最小项。
- 二月结束边界写到二十八日会漏掉闰日,使用次月一日之前更清楚。
- 只给子查询排序,不明确外层行顺序,不能保证用户结果在电影前面。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 184. 部门工资最高的员工 | 中等 | 同样先按组聚合再选择最高指标,本题分别按评分次数及平均评分筛选,并处理字典序并列。 |
| 178. 分数排名 | 中等 | 排序依据来自聚合后的结果,本题选首项并按名称打破并列,原题给每条分数分配密集名次。 |