메뉴 닫기

MSSQL OFFSET-FETCH로 구현하는 효율적인 페이징 쿼리 방법

MSSQL OFFSET-FETCH로 구현하는 효율적인 페이징 쿼리 방법

💡 대용량 데이터에서도 빠르게 원하는 페이지를 불러오는 SQL 페이징 기법

데이터가 수천, 수만 건 이상 쌓인 테이블에서 원하는 부분만 골라서 보여주는 페이징 기능은 웹 서비스와 앱에서 필수 기능입니다.
특히 MSSQL에서는 OFFSET-FETCH 구문을 사용하면 간결하고 직관적인 페이징 쿼리를 작성할 수 있습니다.
하지만 잘못 사용하면 성능 저하나 예기치 못한 결과가 나올 수 있어 정확한 이해가 필요합니다.
오늘은 OFFSET-FETCH의 기본 문법부터 실전 적용 팁, 그리고 성능을 높이는 방법까지 하나씩 알아보겠습니다.

예를 들어, OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY라는 구문을 사용하면 11번째부터 20번째까지의 행만 가져올 수 있습니다.
이는 특히 페이지 번호 기반으로 데이터를 나누어 보여줄 때 매우 유용하며, ORDER BY와 함께 사용해야 안정적인 결과를 얻을 수 있습니다.
이 글에서는 OFFSET-FETCH의 작동 원리와 주의사항을 상세히 다루어, 실무에서 바로 적용할 수 있도록 돕겠습니다.



🔗 OFFSET-FETCH 기본 개념과 문법

OFFSET-FETCH는 MSSQL 2012부터 도입된 기능으로, 대량 데이터 중 일부만 선택적으로 가져올 수 있도록 해주는 페이징(Paging) 문법입니다.
이 기능은 특히 페이지 번호 기반으로 데이터를 나눠 보여주는 웹 서비스에서 많이 사용됩니다.
쉽게 말해, OFFSET은 데이터를 몇 개 건너뛸지 지정하고, FETCH는 그 다음 몇 개의 데이터를 가져올지를 지정하는 역할을 합니다.

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

CODE BLOCK
SELECT 컬럼명
FROM 테이블명
ORDER BY 정렬기준컬럼
OFFSET {건너뛸  수} ROWS
FETCH NEXT {가져올  수} ROWS ONLY;

예를 들어, OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY를 사용하면 11번째 행부터 20번째 행까지만 가져오게 됩니다.
이는 SQL에서 데이터 페이지 이동을 구현할 때 매우 직관적인 방법입니다.

💡 TIP: OFFSET-FETCH를 사용할 때는 반드시 ORDER BY 절이 필요합니다. ORDER BY가 없으면 실행이 불가능합니다.

또한 OFFSET-FETCH는 TOP 절과 달리 특정 페이지를 바로 접근할 수 있어, 페이지 번호 기반의 네비게이션 구현에 적합합니다.
다만, 데이터 양이 많아질수록 OFFSET 값이 커지면 성능 저하가 발생할 수 있으므로, 적절한 인덱스 설계와 함께 사용하는 것이 좋습니다.

🛠️ 페이징 쿼리 작성 예제

OFFSET-FETCH 구문은 실제로 어떻게 사용할까요?
아래 예제는 회원 테이블에서 가입일 순으로 2페이지 데이터를 가져오는 쿼리입니다.
1페이지는 OFFSET 0, 2페이지는 OFFSET 10으로 설정하면 쉽게 구현할 수 있습니다.

CODE BLOCK
-- 회원 테이블에서 가입일 순으로 2페이지 데이터 조회 (페이지당 10개)
SELECT MemberID, Name, JoinDate
FROM Members
ORDER BY JoinDate DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

위 쿼리는 11번째부터 20번째까지의 데이터를 반환합니다.
실제 서비스에서는 페이지 번호페이지 크기를 변수로 받아 OFFSET과 FETCH에 적용하면 유연하게 구현할 수 있습니다.

CODE BLOCK
DECLARE @PageNumber INT = 3;
DECLARE @RowsPerPage INT = 10;

SELECT MemberID, Name, JoinDate
FROM Members
ORDER BY JoinDate DESC
OFFSET (@PageNumber - 1) * @RowsPerPage ROWS
FETCH NEXT @RowsPerPage ROWS ONLY;

이 방법을 사용하면 3페이지 데이터를 바로 불러올 수 있으며, WHERE 절과 결합하면 특정 조건의 데이터만 페이징 처리할 수 있습니다.
또한 검색 필터와 함께 사용하면 사용자 경험을 크게 향상시킬 수 있습니다.

⚠️ 주의: OFFSET-FETCH를 사용할 때 ORDER BY에 고유한 식별자를 포함하지 않으면 페이지 간 중복 또는 누락이 발생할 수 있습니다.



⚙️ ORDER BY와 함께 사용하는 이유

OFFSET-FETCH는 반드시 ORDER BY 절과 함께 사용해야 합니다.
그 이유는 OFFSET이 데이터를 건너뛸 때 어떤 순서로 건너뛸지를 알아야 하기 때문입니다.
ORDER BY 없이 OFFSET-FETCH를 실행하면 SQL Server에서 오류를 발생시키며 쿼리가 동작하지 않습니다.

예를 들어, 다음과 같이 ORDER BY 없이 OFFSET-FETCH를 실행하면 오류가 발생합니다.

CODE BLOCK
-- 잘못된 예제: ORDER BY가 없음
SELECT MemberID, Name
FROM Members
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

또한 ORDER BY에서 고유값(예: 기본 키 컬럼)을 포함해야 데이터 페이지 간의 일관성을 유지할 수 있습니다.
그렇지 않으면 페이지 이동 시 데이터가 중복되거나 빠질 수 있습니다.

💡 TIP: ORDER BY 절에 기본 키를 포함하면 데이터의 정렬 순서가 안정적으로 유지되어, 페이지를 이동해도 결과가 변하지 않습니다.

예를 들어, 가입일과 회원 ID를 함께 정렬하면 다음과 같이 안정적인 페이징을 구현할 수 있습니다.

CODE BLOCK
SELECT MemberID, Name, JoinDate
FROM Members
ORDER BY JoinDate DESC, MemberID DESC
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

이렇게 하면 동일한 JoinDate를 가진 데이터가 있더라도 MemberID 기준으로 확실히 정렬되어 페이지 간 데이터가 겹치지 않습니다.

🔌 OFFSET-FETCH 성능 최적화 팁

OFFSET-FETCH는 간결하고 편리하지만, OFFSET 값이 커질수록 성능이 저하될 수 있습니다.
특히 수십만 건 이상의 데이터에서 높은 페이지 번호로 접근할 경우, SQL Server가 많은 행을 스캔한 후 결과를 반환하게 되어 시간이 오래 걸립니다.

이 문제를 줄이기 위한 몇 가지 성능 최적화 방법은 다음과 같습니다.

  • 적절한 인덱스를 생성해 ORDER BY 절에서 사용하는 컬럼의 정렬 속도를 높입니다.
  • 🔍OFFSET 값이 큰 경우, 키셋 페이징(Keyset Paging) 기법으로 전환을 고려합니다.
  • 🗂️페이징 대상 데이터를 임시 테이블에 저장한 후, 필요한 범위만 다시 조회합니다.

예를 들어, 키셋 페이징은 마지막으로 본 데이터의 키 값을 기준으로 다음 데이터를 가져오기 때문에 OFFSET이 커져도 성능이 크게 떨어지지 않습니다.

CODE BLOCK
-- 키셋 페이징 예제
SELECT TOP 10 MemberID, Name, JoinDate
FROM Members
WHERE JoinDate < @LastJoinDate
ORDER BY JoinDate DESC;

⚠️ 주의: 성능 최적화 없이 대량 데이터에서 높은 OFFSET을 사용하면 응답 지연이 심해져 서비스 품질에 영향을 줄 수 있습니다.

따라서 OFFSET-FETCH를 사용할 때는 데이터량, 페이지 번호, 인덱스 구성 등을 종합적으로 고려하여 설계하는 것이 중요합니다.



💡 OFFSET-FETCH 대안 비교

OFFSET-FETCH는 직관적이지만, 모든 상황에서 최적의 선택은 아닙니다.
데이터 크기, 성능 요구사항, 구현 난이도에 따라 다른 페이징 기법을 고려해야 할 때도 있습니다.
여기서는 OFFSET-FETCH와 다른 대안들을 비교해 보겠습니다.

기법 장점 단점
OFFSET-FETCH 문법이 간단하고 직관적, 페이지 번호 기반 구현 용이 OFFSET 값이 커질수록 성능 저하
TOP + WHERE 간단한 구조, 소규모 데이터셋에 적합 특정 페이지로 바로 이동 불가
Keyset Paging 대량 데이터에서도 빠른 성능 유지 구현 난이도가 상대적으로 높음

이처럼 각 기법은 장단점이 명확합니다.
OFFSET-FETCH는 빠른 구현과 가독성이 장점이지만, 대규모 데이터 환경에서는 성능 저하 문제가 발생할 수 있습니다.
반면 Keyset Paging은 구현이 복잡하지만, 응답 속도가 일정하게 유지됩니다.

💎 핵심 포인트:
프로젝트의 데이터 규모와 성능 목표를 먼저 정의한 뒤, OFFSET-FETCH 또는 대안을 선택하는 것이 효율적입니다.

결론적으로, OFFSET-FETCH는 소규모~중규모 데이터에서 페이지 이동이 빈번하지 않은 경우에 이상적이며, 대규모 데이터에서는 Keyset Paging 같은 대안을 적극 고려하는 것이 좋습니다.

자주 묻는 질문 (FAQ)

OFFSET-FETCH와 TOP의 차이점은 무엇인가요?
TOP은 상위 N개의 데이터를 가져오지만, OFFSET-FETCH는 특정 위치에서 시작해 원하는 개수만큼 가져올 수 있습니다.
OFFSET-FETCH를 쓰면 성능이 많이 떨어지나요?
데이터량이 많고 OFFSET 값이 클수록 성능 저하가 발생할 수 있습니다. 인덱스 최적화나 Keyset Paging을 고려하면 개선됩니다.
ORDER BY 없이 OFFSET-FETCH를 사용할 수 있나요?
사용할 수 없습니다. 반드시 ORDER BY 절과 함께 작성해야 합니다.
페이징 시 중복 데이터가 나오는 이유는 뭔가요?
ORDER BY 절에 고유한 식별자를 포함하지 않으면 정렬이 불안정해 중복이나 누락이 발생할 수 있습니다.
OFFSET-FETCH 대신 Keyset Paging을 쓰면 좋은 경우는?
데이터가 많고 페이지 이동이 잦은 경우, Keyset Paging이 더 빠르고 안정적입니다.
OFFSET-FETCH에서 FETCH NEXT와 FETCH FIRST 차이가 있나요?
MSSQL에서는 FETCH NEXT와 FETCH FIRST가 동일하게 동작합니다. 둘 다 가져올 행 수를 지정합니다.
OFFSET-FETCH로 무한 스크롤을 구현할 수 있나요?
가능합니다. 단, OFFSET 값이 커질 경우 성능 저하를 대비해 적절한 인덱스와 로드 방식이 필요합니다.
OFFSET-FETCH에서 OFFSET 0과 생략은 같은 의미인가요?
네, OFFSET 0을 명시하거나 생략해도 동일하게 처음부터 데이터를 가져옵니다.

📌 효율적인 MSSQL 페이징 쿼리를 위한 마무리 정리

OFFSET-FETCH는 MSSQL에서 간결하게 페이징을 구현할 수 있는 강력한 도구입니다.
ORDER BY와 함께 사용하면 특정 위치에서 원하는 만큼의 데이터를 쉽게 가져올 수 있어, 웹과 앱의 데이터 리스트 출력에 최적화되어 있습니다.
다만, OFFSET 값이 커질수록 성능 저하가 발생할 수 있으므로, 적절한 인덱스 설계와 Keyset Paging 같은 대안을 함께 고려하는 것이 좋습니다.

이 글에서 OFFSET-FETCH의 기본 문법, 실전 예제, ORDER BY와의 관계, 성능 최적화 방법, 그리고 대안까지 살펴보았습니다.
이제 여러분은 데이터 크기와 서비스 성격에 맞춰 최적의 페이징 기법을 선택할 수 있을 것입니다.
효율적인 쿼리 설계는 사용자 경험을 높이고, 서버 자원 사용량을 줄이는 데 직접적인 영향을 미칩니다.


🏷️ 관련 태그 : MSSQL, OFFSET-FETCH, 페이징쿼리, SQL성능최적화, ORDERBY, KeysetPaging, 데이터베이스, 웹개발, 백엔드, SQL튜닝