본문 바로가기
SQL

SQL ORDER BY VS RANK 함수의 성능 비교: 인덱스 활용과 블록 I/O 관점

by 오근성 2025. 3. 28.
728x90

SQL 쿼리에서 ORDER BY는 단순히 결과를 정렬할 때 사용하고, RANK(Window Function)는 순위를 매길 때 사용합니다.
하지만 대용량 테이블에서 이 둘을 잘못 사용하면 예상보다 큰 I/O와 CPU 자원(정렬 비용)이 들 수 있습니다.
이 글에서는 ORDER BYRANK의 동작 원리를 심층적으로 살펴보고, 인덱스 존재 여부에 따른 Big(O) 시간 복잡도 및 효율적인 사용 사례를 정리해보겠습니다.


1. ORDER BY vs RANK: 블록 읽기(Physical I/O) 방식 및 연산량(Big(O) 표기)

1-a. 인덱싱된 컬럼 사용 시

ORDER BY

  • 인덱스를 완전히 활용할 수 있는 경우
    • 예: ORDER BY 인덱스_컬럼
    • DB 옵티마이저가 인덱스의 정렬 순서를 그대로 활용 → 별도의 정렬 연산 최소화.
    • 일반적으로 O(n) 수준(단순 인덱스 스캔), 데이터량이 커도 인덱스 스캔이 효율적일 수 있음.
  • 추가 WHERE 조건이 있어도 인덱스를 탈 수 있는 경우
    • 예: WHERE 인덱스_컬럼 = ? ORDER BY 인덱스_컬럼
    • 범위 스캔(Range Scan)으로 필요한 레코드만 이미 정렬된 순서로 조회 → 대체로 O(m) (m은 조건을 만족하는 건수).

RANK (Window Function)

  • 인덱스 컬럼으로 PARTITION/ORDER 하는 경우
    • 예:
      SELECT
        RANK() OVER (ORDER BY 인덱스_컬럼) AS rn,
        컬럼A, 컬럼B
      FROM 테이블;
    • 인덱스를 사용하더라도 Window Function 특성상, 동순위 계산(‘RANK’)을 위해 내부적으로 추가 스풀(임시 영역) 혹은 메모리 연산이 발생.
    • 일반적으로 O(n log n) 정렬 + 추가 연산 비용이 들 수 있지만, 일부 DB 엔진에서는 인덱스 활용으로 정렬 부담을 줄이는 최적화가 가능.
    • 그래도 ORDER BY보다 한 단계 더 무거운 연산임을 유의(O(n) ~ O(n log n) 사이로 추정).

1-b. 인덱싱이 안된 컬럼 사용 시

ORDER BY

  • 인덱스가 없는 단일 컬럼 정렬
    • 예: ORDER BY 비인덱스_컬럼
    • 테이블 풀 스캔 후 별도 정렬 → 보통 O(n log n) (정렬 알고리즘 복잡도).
    • 대규모 데이터일수록 물리 I/O 증가 및 정렬에 따른 CPU/메모리 사용량 급증.

RANK (Window Function)

  • 인덱스가 없는 컬럼으로 RANK()
    • 예:
      SELECT
        RANK() OVER (ORDER BY 비인덱스_컬럼) AS rn,
        컬럼A, 컬럼B
      FROM 테이블;
    • 테이블 풀 스캔 + Window Function 수행 → 정렬이 필수적이므로 O(n log n).
    • RANK 계산을 위한 스풀/메모리까지 필요 → 전체 비용이 더 커질 수 있음.

1-c. 인덱싱된 컬럼과 비인덱스 컬럼 동시 사용 시

ORDER BY

  • 인덱스 컬럼 + 비인덱스 컬럼 혼합 정렬
    • 예: ORDER BY 인덱스_컬럼 ASC, 비인덱스_컬럼 DESC
    • 인덱스 일부만 활용 → 최종적으로 전체 레코드를 한 번 더 정렬해야 하므로 O(n log n) 근접.

RANK (Window Function)

  • PARTITION BY 또는 ORDER BY에 인덱스/비인덱스 컬럼 함께 사용
    • 예:
      SELECT
        RANK() OVER (PARTITION BY 인덱스_컬럼2 ORDER BY 인덱스_컬럼1, 비인덱스_컬럼) AS rn
      FROM 테이블;
    • 인덱스가 부분적으로만 매칭되어 정렬 부담이 줄어들 수 있으나, Window 함수 자체가 전체(또는 파티션) 데이터 정렬 + 스캔 필요.
    • 동순위 계산의 특성상 O(n log n) 이상의 비용 발생 가능.

2. 효율적인/비효율적인 사용 사례

ORDER BY

  • 효율적인 사례
    1. 정렬 대상 컬럼이 적절히 인덱싱되어 있을 때
      • ORDER BY 인덱스_컬럼
      • 인덱스 스캔만으로 정렬 생략 가능 → O(n) 내외.
    2. TOP N 쿼리(LIMIT 구문)
      • SELECT * FROM 테이블 ORDER BY 인덱스_컬럼 LIMIT 10
      • 일부 DB에서는 Top N 검색 시 인덱스 활용으로 빠른 결과.
  • 비효율적인 사례
    1. 비인덱스 컬럼 대량 정렬
      • 정렬 연산 자체가 O(n log n) 이상에 임시 영역 사용량 증가.
    2. 다중 컬럼 정렬이 인덱스로 커버되지 않는 경우
      • 인덱스 순서와 다르거나 비인덱스 포함 시 결국 다시 정렬 필요.

RANK (Window Function)

  • 효율적인 사례
    1. 정말로 ‘순위’가 필요한 경우(순위 매김, 동순위 포함)
      • 상위 매출 순위, 시험 점수 순위 등.
      • Window 함수로 직관적인 표현 가능.
    2. PARTITION BY와 ORDER BY가 인덱스로 어느 정도 커버
      • 고급 옵티마이저에서 Window Pushdown 등으로 비용 절감 가능.
  • 비효율적인 사례
    1. 단순 정렬만 필요한데 RANK()를 사용하는 경우
      • 불필요한 동순위 계산/스풀이 추가로 성능 저하.
    2. 대용량 + 복잡 파티션/정렬 (인덱스 미비)
      • 테이블 풀 스캔 + 정렬 + 스풀 → O(n log n) 이상 비용.

3. ORDER BY vs RANK 비교 요약 표

구분 ORDER BY RANK (Window Function)
인덱싱된 컬럼 단일 정렬 인덱스 스캔 활용
대체로 O(n)
인덱스 일부 활용 가능
동순위 계산 추가
O(n) ~ O(n log n)
인덱싱 안된 컬럼 정렬 풀 스캔 + 정렬
O(n log n)
풀 스캔 + Window 정렬
O(n log n) (스풀/메모리 추가)
인덱스/비인덱스 컬럼 혼합 정렬 일부 인덱스 사용 후
최종 정렬 필요
O(n log n)
파티션/정렬 전부 수행
O(n log n)
효율적인 사례 인덱스 컬럼 정렬, Top N 조회 실제 ‘순위’가 필요할 때
(PARTITION + ORDER BY 커버)
비효율적인 사례 비인덱스 컬럼 대량 정렬
다중 컬럼 정렬 미커버
단순 정렬만 필요한데 RANK 사용
대용량 + 복잡 파티션/정렬

결론 및 요약

  • ORDER BY는 결과 정렬에 특화되어 있습니다. 인덱스를 잘 활용하면 불필요한 정렬 연산을 줄일 수 있어 효율이 좋습니다. 하지만 인덱스가 없는 컬럼을 대규모로 정렬하면 O(n log n) 이상의 비용이 들어, 성능에 치명적일 수 있습니다.
  • RANK (Window Function)는 동순위 계산을 포함한 “순위 매김”에 특화되어 있습니다. 단순 정렬이 아니라면 훨씬 직관적으로 구현이 가능하지만, 내부적으로 정렬 + 스풀 과정을 거치므로 O(n log n) 이상의 비용이 발생할 수 있습니다.
  • 인덱스가 있는 컬럼이라도 다중 컬럼 혼합 정렬, PARTITION BY 등의 복합 상황에서는 결국 추가적인 정렬/스캔이 불가피해질 수 있으므로, 인덱스 설계쿼리 구조를 신중히 점검해야 합니다.

TIP: 대용량 테이블에서 정렬과 Window Function은 I/O와 메모리 사용량이 크게 늘어납니다.

  • 사전 인덱스 구조 검토,
  • 필요 시 통계 테이블(서브쿼리나 마트 테이블) 생성,
  • PARTITION 분할 설계 등을 통해 성능 문제를 사전에 방지하세요.

참고 자료

  • Oracle Database SQL Tuning Guide
  • PostgreSQL 공식 문서 (Window Functions 섹션)
  • Microsoft SQL Server 공식 문서 (Window Functions 및 인덱싱 관련)
728x90

댓글