MS-SQL 2012 이전 (11.X 미만)
예전의 MS-SQL (2012 이전)에서는 페이징 쿼리를 하기 위해서는
보통 ROW_NUMBER() 를 사용하여 정렬순으로 번호를 부여한 다음
중첩 쿼리을 이용하여 정렬번호를 기준으로 페이지에 해당하는 글을 불러오는 방법을 주로 썼습니다.
# ROW_NUMBER() + 중첩 쿼리를 이용한 기존 방식
SELECT T.MEMBER_ID
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY MEMBER_ID) AS ROWNUM,
MEMBER_ID
FROM MEMBER_TABLE WITH (NOLOCK)
) T
WHERE T.ROWNUM BETWEEN ({pageNo} - 1) * {pageSize} + 1 AND {pageNo} * {pageSize}
위 쿼리에서 MEM_ID로 우선 정렬한 후 ROW_NUMBER()를 이용해 ROWNUM을 부여하고,
BETWEEN 등을 사용하여 탐색하려는 페이지의 첫 번째 ROWNUM부터 페이지의 마지막 ROWNUM 사이를 가져오는 식으로
탐색했습니다.
# 페이지 당 10개 씩 가져올 때
# 1페이지
SELECT T.MEMBER_ID
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY MEMBER_ID) AS ROWNUM,
MEMBER_ID
FROM MEMBER_TABLE WITH (NOLOCK)
) T
WHERE T.ROWNUM BETWEEN 1 AND 10
# 5페이지
SELECT T.MEMBER_ID
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY MEMBER_ID) AS ROWNUM,
MEMBER_ID
FROM MEMBER_TABLE WITH (NOLOCK)
) T
WHERE T.ROWNUM BETWEEN 41 AND 50
MS-SQL 2012 이후 (11.X 이상)
MS-SQL 2012 이후에 추가된 ORDER BY 절의 OFFSET - FETCH 문을 통해
중첩 쿼리 없이 한 번에 가져올 수 있습니다.
# 양식
ORDER BY {정렬 대상 컬럼}
OFFSET {건너 뛸 행의 수} ROWS
FETCH NEXT {가져올 행의 수} ROWS ONLY
ORDER BY를 통해 정렬 기준을 정한 뒤
OFFSET을 통해 건너 뛸 행의 수를 먼저 설정하고
(1부터 가져올 경우 OFFSET 0 ROWS, 11부터 가져올 경우 OFFSET 10 ROWS)
FETCH NEXT 에서 몇 개의 행을 가져올 지 결정하게 됩니다.
(한 페이지 내에 10개를 가져올 경우 FETCH NEXT 10 ROWS ONLY)
# OFFSET - FETCH 를 사용한 예
SELECT MEMBER_ID
FROM MEMBER_TABLE WITH (NOLOCK)
ORDER BY MEMBER_ID
OFFSET ({pageNo} - 1) * {pageSize} ROWS
FETCH NEXT {pageSize} ROWS ONLY
# 1페이지
SELECT MEMBER_ID
FROM MEMBER_TABLE WITH (NOLOCK)
ORDER BY MEMBER_ID
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY
# 5페이지
SELECT MEMBER_ID
FROM MEMBER_TABLE WITH (NOLOCK)
ORDER BY MEMBER_ID
OFFSET 40 ROWS
FETCH NEXT 10 ROWS ONLY
중첩 쿼리를 쓰지 않아서 실행계획도 훨씬 간단해집니다.
그림 1) ROW_NUMBER() + 중첩쿼리 사용한 경우 실행 계획
그림 2) OFFSET - FETCH 적용하여 단일 쿼리로 수행한 경우 실행 계획
쿼리도 간단해지고 성능 상의 이점도 가질 수 있습니다.
참고
Microsoft Docs: docs.microsoft.com/ko-kr/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-ver15