본문 바로가기
Data Science/SQL

[SQL/오답] TOP 1 추출과 데이터 결합: 정렬 조건(Tie-breaker)의 중요성 (LeetCode1341 Medium)

by 에르모사 쩐뉴 2026. 2. 10.

[리트코드]|1341. Movie Rating

https://leetcode.com/problems/movie-rating/description/

1. Problem

두 가지 서로 다른 질문에 대한 답을 구한 뒤, 하나의 결과 열(results)로 합쳐야 한다.

  1. 최다 리뷰어: 가장 많은 리뷰를 남긴 사용자 (동점 시 이름순 정렬)
  2. 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 절이 적합하다.