공공부하자개발 · 영어 학습 노트
SQL
SQL 고급분석 함수·계층·피벗·집계 확장0/10 완료
  • 01순위 분석 함수
  • 02집계 분석 함수와 윈도 프레임
  • 03행 비교 분석 함수
  • 04계층 쿼리: CONNECT BY 와 재귀 CTE
  • 05행과 열 바꾸기
  • 06소계와 총계
  • 07WITH 절(CTE)로 쿼리 구조화
  • 08MERGE 와 UPSERT
  • 09정규식 함수
  • 10다른 테이블 기준으로 수정·삭제
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › SQL 고급 › 01 / 10

순위 분석 함수

ROW_NUMBER, RANK, DENSE_RANK, NTILE
섹션 6진행 0 / 10
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6정리‹ 이전다음 ›

4. 응용 변형 예제

변형 1: CTE 로 같은 Top-N 쓰기

인라인 뷰 대신 WITH 로 이름을 붙이면 안쪽 쿼리를 위에서 읽을 수 있어 길어질수록 편합니다. 여기서는 동점을 함께 살리는 RANK 를 씁니다.

sql
WITH ranked AS (
    SELECT
           dept
         , name
         , sal
         , RANK() OVER (PARTITION BY dept ORDER BY sal DESC) AS rnk
      FROM emp
)
SELECT
       dept
     , name
     , sal
     , rnk
  FROM ranked
 WHERE rnk <= 2
 ORDER BY dept, rnk, name;
text
DEPT | NAME   | SAL  | RNK
-----+--------+------+----
개발 | 이부장 | 800  | 1
개발 | 박과장 | 700  | 2
개발 | 최대리 | 700  | 2
경영 | 김대표 | 1000 | 1
영업 | 정사원 | 600  | 1
영업 | 조사원 | 600  | 1
(6행)

ROW_NUMBER 로 뽑은 5행과 달리 개발에서 700 동점인 최대리가 함께 나와 6행입니다. 영업은 공동 1등 두 명만 나오고 3등(윤사원)은 빠집니다.

변형 2: 동점 처리에 따라 달라지는 Top-N

같은 "상위 2명" 이라도 어떤 함수를 쓰느냐에 따라 결과 행 수가 달라집니다.

sql
SELECT
       t.dept
     , t.name
     , t.sal
     , t.rn
     , t.rnk
  FROM (
        SELECT
               dept
             , name
             , sal
             , ROW_NUMBER() OVER (PARTITION BY dept ORDER BY sal DESC, id) AS rn
             , RANK()       OVER (PARTITION BY dept ORDER BY sal DESC)     AS rnk
          FROM emp
       ) t
 WHERE t.rn <= 2 OR t.rnk <= 2
 ORDER BY t.dept, t.rn;
text
DEPT | NAME   | SAL  | RN | RNK
-----+--------+------+----+----
개발 | 이부장 | 800  | 1  | 1
개발 | 박과장 | 700  | 2  | 2
개발 | 최대리 | 700  | 3  | 2
경영 | 김대표 | 1000 | 1  | 1
영업 | 정사원 | 600  | 1  | 1
영업 | 조사원 | 600  | 2  | 1
(6행)

rn <= 2 는 개발 2명·영업 2명이고, rnk <= 2 는 개발 3명·영업 2명입니다. 최대리는 rn 이 3 이라 ROW_NUMBER 기준에서는 빠지고 RANK 기준에서는 남습니다.

팁

"정확히 N명" 이 필요하면 ROW_NUMBER(고유 컬럼으로 동점 고정), "동점은 모두 포함"이면 RANK 를 씁니다. 요구사항에 동점 처리가 없으면 먼저 확인합니다. Oracle 12c 이상에는 FETCH FIRST n ROWS WITH TIES 도 있지만 여기서는 언급만 합니다.

변형 3: 방언별 Top-N 문법

분석 함수 문법은 네 DB 가 같습니다. 다른 것은 인라인 뷰(파생 테이블) 별칭 규칙입니다. Oracle·Tibero 는 별칭 앞에 AS 를 쓰면 오류이고, MySQL·MSSQL 은 별칭이 필수입니다. 소스: 03_dialects.sql.

sql
-- Oracle · Tibero
SET MODE Oracle;
SELECT
       t.dept
     , t.name
     , t.sal
     , t.rn
  FROM (
        SELECT
               dept
             , name
             , sal
             , ROW_NUMBER() OVER (PARTITION BY dept ORDER BY sal DESC, id) AS rn
          FROM emp
       ) t
 WHERE t.rn <= 2
 ORDER BY t.dept, t.rn;
text
DEPT | NAME   | SAL  | RN
-----+--------+------+---
개발 | 이부장 | 800  | 1
개발 | 박과장 | 700  | 2
경영 | 김대표 | 1000 | 1
영업 | 정사원 | 600  | 1
영업 | 조사원 | 600  | 2
(5행)

MySQL 8.0 과 MSSQL 2005 이상은 같은 문장에서 ) AS t 로 쓰거나 ) t 로 써도 됩니다. 별칭을 빼면 두 DB 모두 오류입니다. SET MODE MySQL·SET MODE MSSQLServer 로 실행한 결과도 위와 같은 5행이었습니다.

주의

H2 는 Oracle 모드에서도 ) AS t 를 허용하고 별칭 없는 파생 테이블도 통과시킵니다. 이 차이는 H2 에서 재현 안 됨, 문법 검토만 하세요. MySQL 5.7 이하에서는 분석 함수 자체가 없으므로 이 문장이 실행되지 않습니다.

변형 4: 같은 키 중 최신 1건만 남기기(중복 제거)

로그 테이블에서 사용자별 가장 최근 로그인 1건만 남기는 패턴입니다. PARTITION BY 에 중복을 판단하는 키를, ORDER BY 에 "남기고 싶은 순서"를 두고 rn = 1 만 고릅니다.

sql
SELECT
       t.id
     , t.user_id
     , t.login_date
  FROM (
        SELECT
               id
             , user_id
             , login_date
             , ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date DESC, id DESC) AS rn
          FROM login_log
       ) t
 WHERE t.rn = 1
 ORDER BY t.user_id;
text
ID | USER_ID | LOGIN_DATE
---+---------+-----------
6  | kim     | 2026-09-09
5  | lee     | 2026-09-07
4  | park    | 2026-09-03
(3행)

같은 날짜가 여럿일 때를 대비해 id DESC 를 덧붙여 결과를 고정했습니다. 이 패턴은 조회뿐 아니라 중복 행을 지우는 DELETE 의 대상 선정에도 그대로 쓰입니다.

응용 변형 예제
  • 변형 1: CTE 로 같은 Top-N 쓰기
  • 변형 2: 동점 처리에 따라 달라지는 Top-N
  • 변형 3: 방언별 Top-N 문법
  • 변형 4: 같은 키 중 최신 1건만 남기기(중복 제거)
이전 섹션3 코드 예제4 / 6다음 섹션5 자주 하는 실수 (Tip)