0Pricing
SQL Interview Prep · 강의

ROW_NUMBER로 고유한 순서 부여하기

각 파티션 안에서 결정적인 행 번호를 부여합니다.

ROW_NUMBER로 고유한 순서 부여하기은(는) CoddyKit의 무료 SQL Interview Prep 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 SQL Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. SQL Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

ROW_NUMBER가 보장하는 것

ROW_NUMBER()는 윈도의 ORDER BY를 따르며 파티션 안의 각 행에 중복 없이 연속된 정수를 1부터 할당합니다. 정렬 기준 값이 같아도 두 행이 같은 번호를 받는 일은 없습니다.

이러한 고유성 때문에 면접관은 "그룹마다 한 행 선택하기"와 중복 제거 문제를 해결할 때 이 함수를 사용합니다. 다른 순위 함수(RANK, DENSE_RANK)는 동률인 행의 번호가 같지 않을 것이라고 보장하지 않습니다.

최소 ROW_NUMBER 쿼리

가장 간단한 형태는 선택한 순서에 따라 전체 결과 집합에 번호를 매깁니다. ROW_NUMBER에는 항상 OVER 내부의 ORDER BY가 필요합니다. 그렇지 않으면 번호가 임의로 매겨지며 대부분의 데이터베이스 엔진은 이를 거부하거나 경고합니다.

여기서는 입사일을 내림차순으로 정렬하므로 가장 최근에 입사한 직원이 행 번호 1을 받습니다.

SELECT
  name,
  hire_date,
  ROW_NUMBER() OVER (ORDER BY hire_date DESC) AS rn
FROM employees;

파티션 내 번호 매기기

PARTITION BY를 추가하면 각 그룹 내부에서 독립적으로 행에 번호를 매길 수 있습니다. 새로운 파티션 값이 나올 때마다 카운터가 1로 재설정됩니다.

이 예제에서는 각 부서가 급여순으로 정렬된 자체 1, 2, 3 순서를 갖습니다. 모든 부서에서 급여가 가장 높은 직원이 1번이 되며, 이것이 그룹별 상위 N개 패턴의 출발점입니다.

SELECT
  department,
  name,
  salary,
  ROW_NUMBER() OVER (
    PARTITION BY department
    ORDER BY salary DESC
  ) AS dept_rn
FROM employees;

ROW_NUMBER와 동률: 동률은 없습니다

면접에서 중요한 지점입니다. 두 행의 정렬 기준 값이 같아도 ROW_NUMBER는 두 행에 서로 다른 번호를 부여합니다. 어느 행이 더 낮은 번호를 받을지는 동률 판별용 열을 추가하지 않는 한 결정적이지 않습니다.

  • RANK는 동률인 행에 같은 순위를 부여합니다.
  • ROW_NUMBER는 동률인 행 사이의 순서를 임의로 정합니다.

결과를 재현 가능하게 만들려면 항상 ORDER BY에 고유한 동률 판별용 열을 추가해야 합니다.

SELECT
  name,
  salary,
  ROW_NUMBER() OVER (
    ORDER BY salary DESC, employee_id  -- employee_id breaks ties deterministically
  ) AS rn
FROM employees;

결정적 동률 판별 기준

ORDER BY가 엄격한 순서를 정하지 못한다면, 즉 열 조합이 고유하지 않다면 같은 데이터에서도 실행할 때마다 행 번호가 달라질 수 있습니다. 면접관은 페이지 매김이나 "최신 레코드" 문제에서 이 함정을 자주 제시합니다.

기억할 규칙은 다음과 같습니다. 결과가 안정적이어야 한다면 정렬 키의 마지막에 기본 키나 다른 고유 열을 추가하세요.

-- Unstable: many rows can share the same created_at
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)

-- Stable: id guarantees a single deterministic winner
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC)

예제 풀이: 고객별 최신 주문

대표적인 질문은 다음과 같습니다. "각 고객의 가장 최근 주문을 반환하세요." 날짜 내림차순으로 고객별 주문에 번호를 매긴 다음, 외부 쿼리에서 rn = 1인 행만 남깁니다.

ROW_NUMBER는 파티션마다 정확히 하나의 행이 1번이 되도록 보장하므로 고객마다 정확히 한 행을 얻습니다. 두 주문의 타임스탬프가 같아도 동률 판별 기준이 순서를 정하기 때문에 중복이 생기지 않습니다.

SELECT customer_id, order_id, order_date, amount
FROM (
  SELECT
    customer_id, order_id, order_date, amount,
    ROW_NUMBER() OVER (
      PARTITION BY customer_id
      ORDER BY order_date DESC, order_id DESC
    ) AS rn
  FROM orders
) t
WHERE rn = 1;

예제 풀이: 행 중복 제거

ROW_NUMBER는 중복 제거에 가장 깔끔한 도구입니다. 중복을 정의하는 열을 기준으로 파티션을 나누고, 선호 기준으로 정렬한 다음 rn = 1만 남깁니다.

여기서는 이메일이 같은 행을 중복으로 간주하고 가장 먼저 생성된 행을 남깁니다. rn > 1인 모든 행은 삭제하거나 무시할 수 있는 중복입니다.

SELECT id, email, created_at
FROM (
  SELECT
    id, email, created_at,
    ROW_NUMBER() OVER (
      PARTITION BY email
      ORDER BY created_at ASC, id ASC
    ) AS rn
  FROM users
) t
WHERE rn = 1;

페이지 매김을 위한 ROW_NUMBER

OFFSET/FETCH가 보편화되기 전에는 ROW_NUMBER가 페이지 매김을 처리했으며, 지금도 SQL Server와 많은 ETL 작업에서 사용됩니다. 안정적인 순서로 행에 번호를 매긴 다음 외부 쿼리에서 번호 범위를 필터링합니다.

면접에서 주의할 점은 정렬 순서가 결정적일 때만 페이지 매김이 정확하다는 것입니다. 그렇지 않으면 같은 행이 두 페이지에 나타나거나 완전히 누락될 수 있습니다.

SELECT *
FROM (
  SELECT *, ROW_NUMBER() OVER (ORDER BY created_at, id) AS rn
  FROM articles
) t
WHERE rn BETWEEN 21 AND 40;  -- page 2, 20 per page

데이터 순서를 다시 정하기 위한 순서 할당

정렬한 뒤 행에 다시 번호를 매기거나, 1부터 N까지의 레이블을 만들거나, 행을 생성된 시퀀스와 짝지어야 하는 등 순차적인 색인만 필요한 경우가 있습니다. 원하는 순서에 ROW_NUMBER를 적용하면 빈틈없는 깔끔한 순차 번호를 얻을 수 있습니다.

순서가 정해지지 않은 임의의 집합에 나중에 조인할 수 있는 안정적인 위치를 부여할 때도 이 방법을 사용합니다.

SELECT
  ROW_NUMBER() OVER (ORDER BY score DESC, player_id) AS leaderboard_position,
  player_id,
  score
FROM scores;

‘N번째 행’에서 ROW_NUMBER와 COUNT 비교

"가장 최근 주문 중 세 번째"나 "두 번째 행"을 요청받았다면 ROW_NUMBER를 사용하고 외부 쿼리에서 정확한 값으로 필터링하세요. 번호가 고유하므로 rn = 3은 정확히 한 행을 반환합니다.

RANK와 비교해 보세요. 동률을 포함해 두 번째로 높은 급여처럼 두 번째 서로 다른 값을 원한다면 ROW_NUMBER는 잘못된 도구입니다. 이 경우에는 다음 수업에서 다룰 DENSE_RANK가 필요합니다.

SELECT order_id, order_date
FROM (
  SELECT order_id, order_date,
         ROW_NUMBER() OVER (ORDER BY order_date DESC, order_id DESC) AS rn
  FROM orders
) t
WHERE rn = 3;  -- exactly the 3rd most recent order

함정 복습

면접에서 긴장하더라도 다음 내용을 혼동하지 마세요.

  • ROW_NUMBER는 파티션 안에서 항상 고유하고 빈틈이 없습니다.
  • OVER에 ORDER BY가 필요합니다. 고유한 동률 판별 기준이 없으면 동률 상황에서 결과가 결정적이지 않습니다.
  • WHERE에서 필터링할 수 없으므로 하위 쿼리/CTE로 감싸야 합니다.
  • 그룹마다 한 행 선택하기, 중복 제거, 페이지 매김에는 이 함수를 사용하고, 동률인 행에 같은 번호를 부여해야 할 때는 DENSE_RANK를 사용합니다.

빠른 확인

ORDER BY 값이 같은 행을 ROW_NUMBER는 어떻게 처리할까요?

복습: 결정적 순서 부여

ROW_NUMBER는 파티션 안에서 고유하고 빈틈없는 번호를 매길 때 사용하는 도구입니다. 다음 방법을 배웠습니다.

  • 전체 집합과 파티션별 그룹에 번호를 매깁니다.
  • 결정적이고 재현 가능한 결과를 위해 고유한 동률 판별 기준을 추가합니다.
  • 외부 쿼리에서 rn을 필터링하여 키별 최신 행, 중복 제거, 페이지 매김, N번째 행 문제를 해결합니다.

다음에는 RANK와 DENSE_RANK가 동률인 행에 의도적으로 같은 번호를 부여하는 방식과 두 함수의 간격 처리 차이를 알아봅니다.

자주 묻는 질문

“ROW_NUMBER로 고유한 순서 부여하기” 강의는 무료인가요?

네 — “ROW_NUMBER로 고유한 순서 부여하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 SQL Interview Prep 강의 전체를 잠금 해제할 수 있습니다. SQL Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

“ROW_NUMBER로 고유한 순서 부여하기”에서 뭘 배우나요?

각 파티션 안에서 결정적인 행 번호를 부여합니다. 브라우저에서 직접 실행하는 실습 코드로 SQL Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

SQL Interview Prep을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 SQL Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“ROW_NUMBER로 고유한 순서 부여하기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 SQL Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 SQL Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. OVER, PARTITION BY와 ORDER BY
  2. ROW_NUMBER로 고유한 순서 부여하기
  3. 동점 상황에서 RANK와 DENSE_RANK 비교하기
  4. 윈도 결과로 필터링하기
← SQL Interview Prep(으)로 돌아가기