Oracle ROWNUM과 ROW_NUMBER 차이점 및 사용법

Oracle ROWNUM과 ROW_NUMBER 차이점 및 사용법

Oracle에서 조회 결과에 번호를 붙이거나 특정 개수의 데이터만 가져오려고 할 때 ROWNUM과 ROW_NUMBER()를 자주 사용합니다.

두 기능 모두 결과에 순번을 부여할 수 있기 때문에 처음 사용할 때는 비슷해 보입니다.

하지만 실제로는 동작하는 방식과 사용하는 목적에 차이가 있습니다.

특히 ORDER BY와 함께 사용할 때 이 차이를 제대로 이해하지 못하면 예상과 다른 결과가 나올 수 있습니다.

이번 글에서는 Oracle의 ROWNUM과 ROW_NUMBER()가 어떻게 다른지, 각각 어떤 상황에서 사용하는 것이 좋은지 예제를 통해 알아보겠습니다.

ROWNUM이란?

ROWNUM은 Oracle이 조회된 행에 순서대로 부여하는 가상 컬럼입니다.

별도의 함수를 호출하지 않고 바로 사용할 수 있습니다.

예를 들어 사원 목록에 번호를 붙여 조회하려면 다음과 같이 작성할 수 있습니다.

SELECT
    ROWNUM AS RN,
    EMPNO,
    ENAME,
    SAL
FROM EMP;

결과는 다음과 같이 나올 수 있습니다.

RNEMPNOENAMESAL
11001홍길동3000
21002김철수4500
31003이영희2500
41004박민수5000

조회된 데이터에 1부터 순서대로 번호가 붙습니다.

하지만 여기서 중요한 점이 있습니다.

ROWNUM은 단순히 화면에 보이는 최종 결과를 기준으로 번호를 붙이는 기능이라고 생각하면 안 됩니다.

이 때문에 ORDER BY와 함께 사용할 때 주의해야 합니다.

ROWNUM으로 조회 개수 제한하기

ROWNUM은 과거 Oracle에서 조회할 데이터의 개수를 제한할 때 많이 사용했습니다.

예를 들어 사원 데이터 중 5개만 조회하려면 다음과 같이 작성할 수 있습니다.

SELECT
    EMPNO,
    ENAME,
    SAL
FROM EMP
WHERE ROWNUM <= 5;

이렇게 하면 조건을 만족하는 데이터 중 최대 5건만 조회됩니다.

단순하게

“전체 데이터 중 몇 건만 가져오고 싶다.”

라는 경우에는 간단하게 사용할 수 있습니다.

ROWNUM과 ORDER BY를 같이 사용할 때 주의할 점

ROWNUM을 사용할 때 가장 많이 헷갈리는 부분입니다.

예를 들어 급여가 가장 높은 사원 3명을 조회하고 싶다고 가정해보겠습니다.

다음과 같이 작성하면 될 것처럼 보입니다.

SELECT
    ROWNUM AS RN,
    EMPNO,
    ENAME,
    SAL
FROM EMP
WHERE ROWNUM <= 3
ORDER BY SAL DESC;

하지만 이 쿼리가 전체 사원 중 급여가 높은 3명을 가져오는 것을 보장하지는 않습니다.

왜냐하면 원하는 상위 데이터를 정렬해서 고르는 것과 ROWNUM을 제한하는 순서를 생각해야 하기 때문입니다.

따라서 먼저 급여순으로 정렬한 뒤 바깥쪽에서 ROWNUM을 적용하는 방식으로 작성할 수 있습니다.

SELECT
    ROWNUM AS RN,
    EMPNO,
    ENAME,
    SAL
FROM (
    SELECT
        EMPNO,
        ENAME,
        SAL
    FROM EMP
    ORDER BY SAL DESC
)
WHERE ROWNUM <= 3;

결과

RNEMPNOENAMESAL
11004박민수5000
21002김철수4500
31001홍길동3000

이렇게 하면 먼저 급여를 내림차순으로 정렬하고 그 결과에서 3건을 가져오게 됩니다.

ROW_NUMBER()란?

ROW_NUMBER()는 분석 함수 중 하나로, 지정한 정렬 기준에 따라 각 행에 순번을 부여합니다.

기본 문법은 다음과 같습니다.

ROW_NUMBER() OVER (ORDER BY 정렬컬럼)

예를 들어 급여가 높은 순서대로 번호를 부여하려면 다음과 같이 사용할 수 있습니다.

SELECT
    EMPNO,
    ENAME,
    SAL,
    ROW_NUMBER() OVER (ORDER BY SAL DESC) AS RN
FROM EMP;

결과

EMPNOENAMESALRN
1004박민수50001
1002김철수45002
1001홍길동30003
1003이영희25004

ROW_NUMBER()는 OVER()안에 정렬 기준을 직접 지정할 수 있다는 것이 특징입니다.

즉,

ROW_NUMBER() OVER (ORDER BY SAL DESC)

SAL이 높은 순서대로 순번을 부여하겠다

는 의미입니다.

ROW_NUMBER()로 상위 데이터 조회하기

급여가 높은 사원 3명을 ROW_NUMBER()를 이용해 조회할 수도 있습니다.

SELECT
    RN,
    EMPNO,
    ENAME,
    SAL
FROM (
    SELECT
        EMPNO,
        ENAME,
        SAL,
        ROW_NUMBER() OVER (ORDER BY SAL DESC) AS RN
    FROM EMP
)
WHERE RN <= 3;

결과는 다음과 같습니다.

RNEMPNOENAMESAL
11004박민수5000
21002김철수4500
31001홍길동3000

ROWNUM을 사용할 때와 달리 순번을 부여하는 기준이 SQL에 명확하게 표현되어 있다는 장점이 있습니다.


ROWNUM과 ROW_NUMBER() 차이

두 기능의 가장 큰 차이는 순번을 어떤 기준으로 부여하느냐입니다.

구분ROWNUMROW_NUMBER()
종류가상 컬럼분석 함수
번호 부여처리되는 행에 번호 부여지정한 정렬 기준으로 번호 부여
정렬 기준 지정직접 지정 불가OVER(ORDER BY …) 사용
그룹별 번호어려움가능
단순 행 개수 제한편리함가능하지만 상대적으로 복잡
순위/순번 처리제한적적합

단순히 몇 건의 데이터만 가져오는 목적이라면 ROWNUM도 간단하게 사용할 수 있습니다.

반면 특정 정렬 기준이나 그룹을 기준으로 정확한 순번을 만들어야 한다면 ROW_NUMBER()가 훨씬 편리합니다.


ROW_NUMBER()의 장점 – 그룹별 순번

ROW_NUMBER()가 유용한 이유 중 하나는 PARTITION BY를 사용할 수 있다는 점입니다.

예를 들어 부서별로 급여가 높은 순서대로 번호를 매겨보겠습니다.

SELECT
    EMPNO,
    ENAME,
    DEPTNO,
    SAL,
    ROW_NUMBER() OVER (
        PARTITION BY DEPTNO
        ORDER BY SAL DESC
    ) AS RN
FROM EMP;

여기서

PARTITION BY DEPTNO

는 부서별로 데이터를 나눈다는 의미입니다.

그리고 각 부서 안에서

ORDER BY SAL DESC

를 기준으로 다시 번호를 매깁니다.

예를 들어 결과가 다음과 같이 나올 수 있습니다.

EMPNOENAMEDEPTNOSALRN
1002김철수1045001
1001홍길동1030002
1004박민수2050001
1003이영희2025002

부서가 바뀌면 RN이 다시 1부터 시작합니다.

이 기능은 실무에서도 꽤 유용합니다.

예를 들어

  • 카테고리별 최신 게시글 찾기
  • 회원별 최근 주문 찾기
  • 상품별 최근 변경 이력 조회
  • 그룹별 최고값을 가진 데이터 찾기

같은 상황에서 사용할 수 있습니다.


그룹별 가장 높은 데이터 하나만 조회하기

ROW_NUMBER()를 활용하면 각 그룹에서 원하는 데이터 하나만 가져오는 것도 가능합니다.

예를 들어 각 부서에서 급여가 가장 높은 사원 한 명씩 조회해보겠습니다.

SELECT
    EMPNO,
    ENAME,
    DEPTNO,
    SAL
FROM (
    SELECT
        EMPNO,
        ENAME,
        DEPTNO,
        SAL,
        ROW_NUMBER() OVER (
            PARTITION BY DEPTNO
            ORDER BY SAL DESC
        ) AS RN
    FROM EMP
)
WHERE RN = 1;

결과

EMPNOENAMEDEPTNOSAL
1002김철수104500
1004박민수205000

개인적으로 ROW_NUMBER()를 사용할 때 단순히 번호를 표시하는 것보다 이런 식으로 그룹별 최신 데이터나 상위 데이터 한 건을 찾는 용도로 사용하는 경우가 더 많습니다.


RANK(), DENSE_RANK()와는 무엇이 다를까?

ROW_NUMBER()와 함께 자주 등장하는 함수가 RANK()와 DENSE_RANK()입니다.

차이는 동일한 값이 있을 때 나타납니다.

급여가 다음과 같다고 가정해보겠습니다.

5000
4000
4000
3000

각 함수를 적용하면 다음과 같습니다.

SALROW_NUMBERRANKDENSE_RANK
5000111
4000222
4000322
3000443

ROW_NUMBER()는 값이 같더라도 무조건 서로 다른 번호를 부여합니다.

RANK()는 같은 값에 같은 순위를 부여하고 다음 순위를 건너뜁니다.

DENSE_RANK()는 같은 값에 같은 순위를 부여하지만 다음 순위를 건너뛰지 않습니다.

따라서 순위가 아니라 각 행에 고유한 순번이 필요하다면 ROW_NUMBER()를 사용하는 것이 적합합니다.


언제 ROWNUM을 사용할까?

단순하게 조회되는 데이터의 개수를 제한할 때 사용할 수 있습니다.

WHERE ROWNUM <= 10

특히 기존 Oracle SQL을 유지보수하다 보면 ROWNUM을 사용한 쿼리를 자주 볼 수 있습니다.

다만 최신 Oracle에서는 단순 행 제한에 FETCH FIRST문법을 사용할 수도 있습니다.

예를 들어 다음과 같습니다.

SELECT
    EMPNO,
    ENAME,
    SAL
FROM EMP
ORDER BY SAL DESC
FETCH FIRST 10 ROWS ONLY;

단순 TOP-N 조회라면 이런 방식도 알아두면 좋습니다.


언제 ROW_NUMBER()를 사용할까?

다음처럼 데이터의 순서 자체가 중요한 경우에 적합합니다.

  • 특정 기준으로 번호를 부여할 때
  • 그룹별 순위를 만들 때
  • 그룹별 최신 데이터 한 건을 찾을 때
  • 중복 데이터 중 한 건을 선택할 때
  • 페이징 처리를 할 때

특히 PARTITION BY와 함께 사용할 수 있다는 점 때문에 복잡한 조회에서는 ROWNUM보다 활용도가 높습니다.


ROWNUM과 ROW_NUMBER(), 무엇을 사용해야 할까?

둘 중 하나가 무조건 더 좋다고 보기는 어렵습니다.

단순히 조회 결과를 몇 건으로 제한하려는 목적이라면 ROWNUM이나 최신 Oracle의 FETCH FIRST가 간단합니다.

반대로

“급여가 높은 순서대로 번호를 매기고 싶다.”

또는

“부서별로 가장 급여가 높은 사원 한 명을 찾고 싶다.”

처럼 명확한 기준에 따라 순번을 만들어야 한다면 ROW_NUMBER()가 적합합니다.

결국 중요한 것은 번호를 붙이는 목적입니다.


요약

Oracle의 ROWNUM과 ROW_NUMBER()는 모두 조회 결과에서 순번과 관련된 작업에 사용할 수 있지만 동작 방식은 다릅니다.

ROWNUM은 Oracle이 처리하는 행에 부여하는 가상 컬럼으로 단순한 조회 개수 제한 등에 사용할 수 있습니다.

반면 ROW_NUMBER()는 분석 함수이며 ORDER BY를 통해 원하는 기준으로 순번을 부여할 수 있고 PARTITION BY를 이용해 그룹별 순번도 만들 수 있습니다.

따라서 단순 행 제한에는 ROWNUM을 사용할 수 있고, 정렬 기준이나 그룹별 순번이 필요한 경우에는 ROW_NUMBER()를 사용하는 것이 편리합니다.

특히 ROWNUM과 ORDER BY를 함께 사용할 때는 처리 순서 때문에 예상과 다른 결과가 나올 수 있으므로 주의해야 합니다.


자주 묻는 질문(FAQ)

Q. ROWNUM과 ROW_NUMBER()는 같은 기능인가요?

아닙니다. 둘 다 번호와 관련된 기능이지만 ROWNUM은 Oracle의 가상 컬럼이고, ROW_NUMBER()는 정렬 기준에 따라 번호를 부여하는 분석 함수입니다.

Q. ROWNUM으로 상위 10개 데이터를 조회할 수 있나요?

가능합니다. 다만 특정 컬럼을 정렬한 후 상위 10개를 가져오려면 먼저 정렬한 결과에 ROWNUM을 적용해야 합니다.

Q. ROW_NUMBER()에서 같은 값이 있으면 같은 번호가 나오나요?

아닙니다. 같은 값이 있어도 각각 다른 번호가 부여됩니다. 같은 값에 같은 순위를 부여하려면 RANK()나 DENSE_RANK()를 사용할 수 있습니다.

Q. ROWNUM 대신 ROW_NUMBER()만 사용해도 되나요?

목적에 따라 다릅니다. 복잡한 순번 처리에는 ROW_NUMBER()가 유용하지만 단순한 행 개수 제한이라면 ROWNUM이나 FETCH FIRST가 더 간단할 수 있습니다.

Q. 부서별로 가장 급여가 높은 사원을 찾으려면 어떤 것을 사용해야 하나요?

ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY SAL DESC)와 같이 작성한 뒤 RN이 1인 데이터만 조회하는 방법을 사용할 수 있습니다.

함께 보면 좋은 Oracle 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다