728x90
SQL 쿼리에서 ORDER BY는 단순히 결과를 정렬할 때 사용하고, RANK(Window Function)는 순위를 매길 때 사용합니다.
하지만 대용량 테이블에서 이 둘을 잘못 사용하면 예상보다 큰 I/O와 CPU 자원(정렬 비용)이 들 수 있습니다.
이 글에서는 ORDER BY와 RANK의 동작 원리를 심층적으로 살펴보고, 인덱스 존재 여부에 따른 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
- 효율적인 사례
- 정렬 대상 컬럼이 적절히 인덱싱되어 있을 때
ORDER BY 인덱스_컬럼
- 인덱스 스캔만으로 정렬 생략 가능 → O(n) 내외.
- TOP N 쿼리(LIMIT 구문)
SELECT * FROM 테이블 ORDER BY 인덱스_컬럼 LIMIT 10
- 일부 DB에서는 Top N 검색 시 인덱스 활용으로 빠른 결과.
- 정렬 대상 컬럼이 적절히 인덱싱되어 있을 때
- 비효율적인 사례
- 비인덱스 컬럼 대량 정렬
- 정렬 연산 자체가 O(n log n) 이상에 임시 영역 사용량 증가.
- 다중 컬럼 정렬이 인덱스로 커버되지 않는 경우
- 인덱스 순서와 다르거나 비인덱스 포함 시 결국 다시 정렬 필요.
- 비인덱스 컬럼 대량 정렬
RANK (Window Function)
- 효율적인 사례
- 정말로 ‘순위’가 필요한 경우(순위 매김, 동순위 포함)
- 상위 매출 순위, 시험 점수 순위 등.
- Window 함수로 직관적인 표현 가능.
- PARTITION BY와 ORDER BY가 인덱스로 어느 정도 커버
- 고급 옵티마이저에서 Window Pushdown 등으로 비용 절감 가능.
- 정말로 ‘순위’가 필요한 경우(순위 매김, 동순위 포함)
- 비효율적인 사례
- 단순 정렬만 필요한데 RANK()를 사용하는 경우
- 불필요한 동순위 계산/스풀이 추가로 성능 저하.
- 대용량 + 복잡 파티션/정렬 (인덱스 미비)
- 테이블 풀 스캔 + 정렬 + 스풀 → O(n log n) 이상 비용.
- 단순 정렬만 필요한데 RANK()를 사용하는 경우
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
'SQL' 카테고리의 다른 글
용어 정리: DW·OLAP에서 Zero-Copy 공유까지 (1) | 2025.06.16 |
---|---|
DBMS 내부 구조와 효율적 데이터 관리 방식 (0) | 2025.03.31 |
댓글