데이터베이스의 규모가 커질수록 SELECT *로 모든 데이터를 가져오는 것은 불가능에 가깝습니다.
수백만 건의 데이터 중 사용자가 보고 있는 10~20건만 효율적으로 골라내는 MSSQL 페이징 기법 3가지를 소개합니다.
1. OFFSET-FETCH (SQL Server 2012 이상 권장)
ANSI 표준이며 현재 MSSQL에서 가장 권장되는 방식입니다. 구문이 직관적이고 가독성이 매우 높습니다.
- 특징: 반드시 ORDER BY 절과 함께 사용해야 합니다.
- 장점: 코드가 간결하며 유지보수가 쉽습니다.
SQL
-- 11번째부터 10개의 행을 가져오는 예시 (Page 2)
SELECT *
FROM Orders
ORDER BY OrderDate DESC, OrderID ASC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
2. ROW_NUMBER() 함수 활용 (전통적 방식)
OFFSET-FETCH가 도입되기 전 가장 많이 사용되던 방식입니다. 서브쿼리를 사용하여 순번을 매긴 뒤 범위를 필터링합니다.
- 특징: OVER(ORDER BY ...)를 통해 정렬 기준을 정의합니다.
- 장점: 복잡한 조건부 페이징이나 하위 버전(2005, 2008)과의 호환성이 좋습니다.
SQL
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY OrderDate DESC) AS RowNum
FROM Orders
) AS PageResults
WHERE RowNum BETWEEN 11 AND 20;
3. TOP과 NOT IN을 이용한 방식 (구형/비권장)
아주 오래된 방식이지만 간혹 레거시 코드에서 볼 수 있습니다. 앞선 10개를 제외한 나머지 중 상위 10개를 가져오는 식입니다.
- 단점: 데이터량이 많아질수록 NOT IN 내부의 스캔 비용이 기하급수적으로 늘어나 성능이 가장 좋지 않습니다.
SQL
-- 비권장 방식
SELECT TOP 10 * FROM Orders
WHERE OrderID NOT IN (SELECT TOP 10 OrderID FROM Orders ORDER BY OrderDate DESC)
ORDER BY OrderDate DESC;
🚀 대용량 처리 시 '진짜' 중요한 성능 포인트
단순히 문법만 바꾼다고 속도가 빨라지지 않습니다. MSSQL 엔진이 데이터를 찾는 방식을 이해해야 합니다.
- 인덱스 검색 (Index Seek) 유도:
- 페이징의 기준이 되는 ORDER BY 컬럼은 반드시 인덱스가 걸려 있어야 합니다.
인덱스가 없다면 전체 데이터를 정렬(Sort)하는 부하가 발생하여 페이징의 의미가 퇴색됩니다. - 커서 기반 페이징 (Seek Method):
- 예: WHERE ID < @LastSeenID ORDER BY ID DESC
- OFFSET 값이 커질수록(예: 1,000,000번째 페이지) DB는 앞의 백만 개를 읽어서 버려야 하므로 느려집니다.
이때는 마지막으로 읽은 고유값(예: ID)을 조건문에 넣는 방식이 가장 빠릅니다. - 포함된 열(Included Columns):
- SELECT * 대신 필요한 컬럼만 조회하고, 해당 컬럼들을 인덱스에 포함(INCLUDE)시키면 테이블 자체에 접근하지 않고 인덱스만으로 데이터를 반환하는 '커버링 인덱스' 효과를 볼 수 있습니다.
💡 요약 및 결론
| 방식 | 추천 버전 | 성능 | 가독성 |
| OFFSET-FETCH | 2012+ | 우수 | 최상 |
| ROW_NUMBER() | 2005+ | 우수 | 보통 |
| Seek Method | 공통 | 최상 | 낮음 (로직 복잡) |
최신 프로젝트라면 OFFSET-FETCH를 기본으로 사용하되, 초대용량 테이블에서 뒷페이지 조회 성능이 떨어진다면 Seek Method(ID 기반 조건문) 도입을 검토하세요.
Copyright 2026. [버미] all rights reserved.