[리트코드]|1341. Movie Rating
https://leetcode.com/problems/movie-rating/description/
1. Problem
두 가지 서로 다른 질문에 대한 답을 구한 뒤, 하나의 결과 열(results)로 합쳐야 한다.
- 최다 리뷰어: 가장 많은 리뷰를 남긴 사용자 (동점 시 이름순 정렬)
- 2월 최고 평점 영화: 2020년 2월 평균 평점이 가장 높은 영화 (동점 시 제목순 정렬)
2. Solution: 개별 쿼리 최적화와 통합
각 요구사항을 독립적인 서브쿼리로 작성하여 LIMIT 1으로 최상위 데이터를 뽑고, UNION ALL로 연결한다.
3. Takeaway: 실전 쿼리 작성을 위한 객관적 분석
- 집계 중첩의 한계와 대안: SQL은 MAX(AVG(rating))과 같은 중첩 집계를 허용하지 않는다. 대신 유진 님의 분석처럼 **ORDER BY와 LIMIT 1**을 조합하면 최댓값을 가진 행을 가장 직관적이고 효율적으로 추출할 수 있다.
- 정교한 정렬(Tie-breaker): 문제에서 "동점일 경우 사전 순"이라는 조건이 붙으면 반드시 ORDER BY에 두 번째 기준을 명시해야 한다. 이를 누락하면 엔진에 따라 결과가 달라져 데이터 일관성이 깨질 수 있다.
- 날짜 필터링 전략: WHERE 절에서 LIKE '2020-02%'를 사용하여 집계(AVG)를 수행하기 전에 미리 행을 줄이는 것이 성능상 유리하다. 이는 대용량 데이터 처리 시 필수적인 최적화 습관이다.
Table: Movies
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| movie_id | int |
| title | varchar |
+---------------+---------+
movie_id is the primary key (column with unique values) for this table.
title is the name of the movie.
Each movie has a unique title.
Table: Users
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| user_id | int |
| name | varchar |
+---------------+---------+
user_id is the primary key (column with unique values) for this table.
The column 'name' has unique values.
Table: MovieRating
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| movie_id | int |
| user_id | int |
| rating | int |
| created_at | date |
+---------------+---------+
(movie_id, user_id) is the primary key (column with unique values) for this table.
This table contains the rating of a movie by a user in their review.
created_at is the users review date.
Write a solution to:
Find the name of the user who has rated the greatest number of movies. In case of a tie, return the lexicographically smaller user name.
Find the movie name with the highest average rating in February 2020. In case of a tie, return the lexicographically smaller movie name.
The result format is in the following example.
Example 1:
Input:
Movies table:
+-------------+--------------+
| movie_id | title |
+-------------+--------------+
| 1 | Avengers |
| 2 | Frozen 2 |
| 3 | Joker |
+-------------+--------------+
Users table:
+-------------+--------------+
| user_id | name |
+-------------+--------------+
| 1 | Daniel |
| 2 | Monica |
| 3 | Maria |
| 4 | James |
+-------------+--------------+
MovieRating table:
+-------------+--------------+--------------+-------------+
| movie_id | user_id | rating | created_at |
+-------------+--------------+--------------+-------------+
| 1 | 1 | 3 | 2020-01-12 |
| 1 | 2 | 4 | 2020-02-11 |
| 1 | 3 | 2 | 2020-02-12 |
| 1 | 4 | 1 | 2020-01-01 |
| 2 | 1 | 5 | 2020-02-17 |
| 2 | 2 | 2 | 2020-02-01 |
| 2 | 3 | 2 | 2020-03-01 |
| 3 | 1 | 3 | 2020-02-22 |
| 3 | 2 | 4 | 2020-02-25 |
+-------------+--------------+--------------+-------------+
Output:
+--------------+
| results |
+--------------+
| Daniel |
| Frozen 2 |
+--------------+
Explanation:
Daniel and Monica have rated 3 movies ("Avengers", "Frozen 2" and "Joker") but Daniel is smaller lexicographically.
Frozen 2 and Joker have a rating average of 3.5 in February but Frozen 2 is smaller lexicographically.
정답 쿼리
(SELECT a.name AS results
FROM
(SELECT u.name, COUNT(*) AS results
FROM MovieRating r
JOIN Users u ON r.user_id = u.user_id
GROUP BY r.user_id
ORDER BY results DESC, u.name ASC
LIMIT 1) a
)
UNION ALL
(
SELECT m.title AS results
FROM MovieRating r
JOIN Movies m ON r.movie_id = m.movie_id
WHERE r.created_at LIKE '2020-02%'
GROUP BY r.movie_id
ORDER BY AVG(r.rating) DESC, m.title ASC
LIMIT 1)
;
1. 내가 틀렸던 부분 (복기용)
- 집계함수의 중첩 사용 금지: MAX(AVG(rating))처럼 함수 안에 함수를 쓰려고 했다. SQL은 집계된 결과 집합을 다시 집계하려면 서브쿼리를 쓰거나 정렬(ORDER BY) + 개수 제한(LIMIT) 전략을 써야 한다.
- 타이 브레이커(Tie-breaker)의 부재: "동점일 경우 사전 순 정렬" 조건을 간과했다. ORDER BY에 1순위(숫자) 외에 2순위(이름/제목) 기준을 명시하지 않으면 결과가 랜덤하게 나와 오답이 된다.
- 날짜 필터링의 위치: 두 번째 쿼리에서 '2020년 2월' 조건을 빠뜨렸다. 문제의 요구사항을 꼼꼼히 체크하는 습관이 필요하다.
- 서브쿼리 Alias 참조 오류: 서브쿼리 바깥에서 정의한 별칭(b.title)을 안쪽 ORDER BY에서 사용하려고 했다. 정렬은 원본 컬럼명이나 서브쿼리 내부 별칭을 사용해야 한다.
2. 이 문제의 핵심 포인트 (Key Concepts)
① TOP 1 추출 전략: ORDER BY + LIMIT 1
가장 높은/낮은 값을 가진 **'행 전체'**를 가져올 때 가장 효율적인 방법이다.
- 최다 평가 유저: ORDER BY COUNT(*) DESC, name ASC LIMIT 1
- 최고 평점 영화: ORDER BY AVG(rating) DESC, title ASC LIMIT 1
② UNION ALL의 활용
두 개의 서로 다른 결과(유저 이름, 영화 제목)를 하나의 열(results)로 합칠 때 사용한다.
- UNION: 중복 제거 (느림)
- UNION ALL: 중복 허용 (빠름) -> 이 문제처럼 결과 성격이 달라 중복 가능성이 없을 때 유리하다.
③ 날짜 필터링 (WHERE vs HAVING)
- WHERE created_at LIKE '2020-02%': 계산하기 전에 미리 데이터를 걸러낸다. (성능상 유리)
- 평균을 구한 뒤에 거르는 것이 아니라, 특정 기간의 데이터만 뽑아서 평균을 내야 하므로 WHERE 절이 적합하다.