ROW_NUMBER로 그룹별 상위 N개 행 찾기
‘범주별 상위 3개’ 문제를 해결하는 대표적인 파티션 및 순위 패턴을 익힙니다.
ROW_NUMBER로 그룹별 상위 N개 행 찾기은(는) CoddyKit의 무료 SQL Interview Prep 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 SQL Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. SQL Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
그룹별 상위 N개 질문
가장 흔한 SQL 면접 질문 중 하나는 간단해 보입니다. "각 부서에서 급여가 가장 높은 직원 3명을 반환하세요." 하지만 LIMIT를 바로 사용하는 지원자는 실패합니다. LIMIT는 각 그룹이 아니라 전체 결과 집합을 제한하기 때문입니다.
면접관은 지원자가 윈도 함수를 알고 있는지 확인합니다. 표준적인 답변은 각 그룹 안에서 행에 번호를 매긴 다음, 번호가 N 이하인 행만 남기는 것입니다. 이 레슨에서는 이 패턴을 단계별로 구성합니다.
LIMIT로 해결할 수 없는 이유
아래 쿼리를 작성한다고 가정해 보겠습니다. 이 쿼리는 부서별로 3개가 아니라 전체 테이블에서 총 3개의 행만 반환합니다.
LIMIT(또는 TOP, FETCH FIRST)는 최종 결과 집합에 적용됩니다. 표준 SQL에는 그룹별 LIMIT가 없습니다. 면접관이 그룹별 문제에 LIMIT 3을 제안하는 말을 들으면, 파티션 분할을 제대로 이해하지 못했다는 신호로 받아들입니다.
-- WRONG: only 3 rows total, not 3 per department
SELECT department, name, salary
FROM employees
ORDER BY salary DESC
LIMIT 3;ROW_NUMBER 알아보기
ROW_NUMBER()는 정렬 순서에 따라 각 행에 고유하고 중복 없이 이어지는 정수를 할당하는 윈도 함수입니다. 그 자체로 사용하면 전체 결과에 번호를 매깁니다.
핵심 요소는 PARTITION BY입니다. 이 요소는 각 그룹마다 번호를 1부터 다시 시작하게 합니다. PARTITION BY department와 ORDER BY salary DESC를 결합하면 각 부서에서 급여순으로 1, 2, 3, ...이라는 고유한 순위가 매겨집니다.
SELECT
name,
department,
salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS rn
FROM employees;번호가 매겨진 결과 읽기
이전 쿼리를 실행하면 모든 행에 rn 값이 포함됩니다. 각 부서에서 급여가 가장 높은 행에는 rn = 1, 다음 행에는 2, 그다음 행에는 계속해서 번호가 매겨집니다. 새로운 부서가 나오면 다시 1부터 시작합니다.
- Sales: Ana (1), Bo (2), Cal (3), Dee (4)
- Engineering: Eve (1), Fin (2), Gus (3)
이제 "부서별 상위 3개"는 간단히 "rn <= 3인 행만 남기기"를 의미합니다.
WHERE에서 rn을 필터링할 수 없는 이유
자연스러운 다음 단계는 WHERE rn <= 3이지만 실패합니다. 논리적 실행 순서에서 윈도 함수는 WHERE 절보다 나중에 계산되므로, WHERE가 실행될 때는 별칭 rn이 아직 존재하지 않습니다.
면접관은 이 함정을 자주 사용합니다. 해결 방법은 하위 쿼리 또는 CTE에서 윈도 함수를 계산한 다음, 외부 쿼리에서 내부 쿼리의 결과를 필터링하는 것입니다.
-- ERROR: rn does not exist in WHERE
SELECT name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
WHERE rn <= 3;표준적인 CTE 해결책
ranked라는 이름의 CTE로 번호 매기기를 감싼 다음, 외부 WHERE에서 필터링하면서 CTE를 조회합니다. 이것이 면접관이 보고 싶어 하는 답변이며, 읽기도 깔끔합니다.
이 뼈대를 외워 두십시오. 그룹으로 파티션을 나누고, 지표로 정렬한 다음, 외부 쿼리에서 rn ≤ N을 필터링합니다. 숫자 하나만 바꾸면 상위 1개, 상위 5개 또는 어떤 N에도 적용할 수 있습니다.
WITH ranked AS (
SELECT
name, department, salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS rn
FROM employees
)
SELECT name, department, salary
FROM ranked
WHERE rn <= 3
ORDER BY department, rn;하위 쿼리 형식
면접관이 사용하는 SQL 방언이 오래되었거나 하위 쿼리를 선호한다면, 동일한 논리를 FROM 안의 파생 테이블에 넣을 수 있습니다. 파생 테이블에는 반드시 별칭이 있어야 한다는 점을 기억하십시오(r이 여기서 사용하는 별칭입니다). 그렇지 않으면 구문 오류가 발생합니다.
CTE 형식과 파생 테이블 형식은 이 문제에서 서로 바꿔 사용할 수 있습니다. 면접관이 더 읽기 쉽다고 느끼는 쪽을 선택하십시오. 두 방식 모두 똑같이 올바릅니다.
SELECT name, department, salary
FROM (
SELECT name, department, salary,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
) AS r
WHERE rn <= 3;그룹별 상위 1개: 최고 항목 하나
"각 부서에서 급여가 가장 높은 직원 한 명을 찾으세요"는 N = 1인 경우일 뿐입니다. 필터를 rn = 1로 설정하십시오.
MAX(salary)와 GROUP BY department를 사용하면 안 되는 이유는 무엇일까요? MAX는 급여 값만 제공하고 해당 직원의 나머지 행 정보(이름, 입사일 등)는 제공하지 않기 때문입니다. ROW_NUMBER는 선정된 직원의 전체 행을 그대로 유지하므로, 일반적으로 질문에서 실제로 원하는 결과를 얻을 수 있습니다.
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
)
SELECT name, department, salary, hire_date
FROM ranked
WHERE rn = 1;결정성을 보장하는 동률 해소 기준 추가
ROW_NUMBER는 급여가 같더라도 항상 정확히 N개의 행을 반환합니다. 하지만 동률을 해소하지 않으면 어떤 동률 행에 rn = 1이 부여될지는 임의적입니다. 두 사람이 90000을 받고 rn = 1인 행만 남긴다면, 선택되는 사람은 실행할 때마다 달라질 수 있습니다.
employee_id와 같은 보조 고유 정렬 키를 추가하면 결과가 안정적이고 재현 가능해집니다. 면접관은 별도의 질문이 없어도 결정성을 언급하는 지원자를 높이 평가합니다.
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC, employee_id ASC
) AS rn구체적인 작업 예시
region, product, revenue 열이 있는 sales 테이블이 주어졌을 때, 지역별 수익 상위 2개 제품을 반환하십시오. 동일한 방식으로 region으로 파티션을 나누고, revenue DESC로 정렬한 다음, rn <= 2인 행만 남깁니다.
바뀌는 것은 파티션 열과 지표 열뿐이라는 점에 주목하십시오. 비즈니스 분야가 달라도 구조는 동일합니다.
WITH ranked AS (
SELECT region, product, revenue,
ROW_NUMBER() OVER (
PARTITION BY region ORDER BY revenue DESC, product
) AS rn
FROM sales
)
SELECT region, product, revenue
FROM ranked
WHERE rn <= 2
ORDER BY region, rn;성능 및 면접에서 언급할 내용
정확성 외에도 좋은 인상을 주려면 다음을 언급하십시오.
(department, salary DESC)에 대한 인덱스가 있으면 엔진이 파티션별로 정렬된 행을 효율적으로 생성하는 데 도움이 됩니다.- 윈도 방식은 테이블을 한 번만 검색하므로 행마다 실행되는 상관 하위 쿼리보다 훨씬 효율적입니다.
- 매우 큰 상위 1개 문제의 경우 일부 엔진은 지름길로
DISTINCT ON(Postgres)을 지원하지만,ROW_NUMBER가 이식 가능한 표준 방식입니다.
동률 해소 기준을 항상 명시하고 요청된 N을 확인하십시오.
빠른 확인
그룹별 상위 N개 패턴에 대한 이해를 확인해 보십시오.
복습: 그룹별 상위 N개
이 패턴을 한 문장으로 요약하면 다음과 같습니다. 그룹으로 파티션을 나누고, 지표로 정렬하고, ROW_NUMBER를 할당한 다음, 외부 쿼리에서 rn ≤ N인 행만 남깁니다.
LIMIT는 전체 집합을 제한하며, 그룹별로 제한하지는 않습니다.- 윈도 별칭은
WHERE에서 필터링할 수 없으므로 CTE 또는 하위 쿼리로 감싸야 합니다. - 결정적인 결과를 얻으려면 고유한 동률 해소 기준을 추가하십시오.
- 상위 1개는
MAX+GROUP BY와 달리 선정된 전체 행을 유지합니다.
숫자 하나만 바꾸면 같은 쿼리로 상위 1개, 상위 5개 또는 어떤 N이든 해결할 수 있습니다.
자주 묻는 질문
“ROW_NUMBER로 그룹별 상위 N개 행 찾기” 강의는 무료인가요?
네 — “ROW_NUMBER로 그룹별 상위 N개 행 찾기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 SQL Interview Prep 강의 전체를 잠금 해제할 수 있습니다. SQL Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.
“ROW_NUMBER로 그룹별 상위 N개 행 찾기”에서 뭘 배우나요?
‘범주별 상위 3개’ 문제를 해결하는 대표적인 파티션 및 순위 패턴을 익힙니다. 브라우저에서 직접 실행하는 실습 코드로 SQL Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
SQL Interview Prep을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 SQL Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“ROW_NUMBER로 그룹별 상위 N개 행 찾기” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 SQL Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 SQL Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- ROW_NUMBER로 그룹별 상위 N개 행 찾기
- 상위 N개 결과에서 동점 처리하기
- 안전하게 행 중복 제거하기
- 키별 최신 행 유지하기