공공부하자개발 · 영어 학습 노트
SQL
SQL 실무페이징·검색·이력·통계·채번·이관0/9 완료
  • 01페이징 쿼리
  • 02동적 검색 조건
  • 03이력 테이블과 시점 조회
  • 04기간별 통계 보고서
  • 05중복 데이터 찾기와 정리
  • 06트랜잭션 기초
  • 07채번과 순번 관리
  • 08저장 프로시저·함수 기초
  • 09데이터 이관·검증 쿼리
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › SQL 실무 › 01 / 9

페이징 쿼리

ROWNUM, LIMIT, OFFSET FETCH, 키셋 페이징
섹션 6진행 0 / 9
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6정리‹ 이전다음 ›

3. 코드 예제

소스: sql-src/work_01_paging/01_offset_paging.sql. 게시판 표 board(id, title, writer, created_at, views)에 27행이 있습니다. 게시글 01~27 이 날짜순으로 쌓여 있고 같은 날짜가 여러 개 있습니다. 페이지 크기는 10 이라 페이지가 3 개입니다.

예제 1: 정렬 동점 확인

같은 created_at 을 가진 행이 있는지 먼저 봅니다.

sql
SELECT
       created_at
     , COUNT(*) AS cnt
  FROM board
 GROUP BY created_at
HAVING COUNT(*) > 1
 ORDER BY created_at;
text
CREATED_AT | CNT
-----------+----
2026-09-07 | 2
2026-09-17 | 2
2026-09-24 | 3
(3행)

9월 7일 2건(7·8번), 17일 2건(17·18번), 24일 3건(24·25·26번)이 동점입니다. 내림차순으로 세면 10번째가 18번, 11번째가 17번이라 페이지 1 과 2 의 경계에 17일 동점이 걸쳐 있습니다. 20번째와 21번째(8번, 7번)도 7일 동점입니다. created_at 만으로 정렬하면 이 경계 행들의 순서가 호출마다 달라질 수 있습니다.

예제 2: 페이지 1 과 총 건수

id 를 덧붙인 정렬로 첫 페이지를 가져오면서 총 건수와 총 페이지 수를 함께 받습니다.

sql
SELECT
       id
     , title
     , created_at
     , COUNT(*) OVER () AS total
     , CEIL(COUNT(*) OVER () / 10.0) AS total_pages
  FROM board
 ORDER BY created_at DESC, id DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;
text
ID | TITLE     | CREATED_AT | TOTAL | TOTAL_PAGES
---+-----------+------------+-------+------------
27 | 게시글 27 | 2026-09-26 | 27    | 3
26 | 게시글 26 | 2026-09-24 | 27    | 3
25 | 게시글 25 | 2026-09-24 | 27    | 3
24 | 게시글 24 | 2026-09-24 | 27    | 3
23 | 게시글 23 | 2026-09-23 | 27    | 3
22 | 게시글 22 | 2026-09-22 | 27    | 3
21 | 게시글 21 | 2026-09-21 | 27    | 3
20 | 게시글 20 | 2026-09-20 | 27    | 3
19 | 게시글 19 | 2026-09-19 | 27    | 3
18 | 게시글 18 | 2026-09-17 | 27    | 3
(10행)

total 은 10행 모두 27 입니다. 분석 함수는 OFFSET·FETCH 로 자르기 전의 전체 행에 적용되기 때문입니다. total_pages 는 27 / 10.0 을 올림한 3 입니다. 10.0 으로 나누는 이유는 MSSQL 처럼 정수끼리 나누면 소수점이 잘리는 DB 를 위해서입니다.

예제 3: 페이지 3

페이지 2 는 OFFSET 10 이고 소스에 있습니다. 결과는 예제 6 과 같은 10 행(17번부터 8번)이라 생략하고, 마지막 페이지인 3 만 보입니다.

sql
SELECT
       id
     , title
     , created_at
     , COUNT(*) OVER () AS total
  FROM board
 ORDER BY created_at DESC, id DESC
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
text
ID | TITLE     | CREATED_AT | TOTAL
---+-----------+------------+------
7  | 게시글 07 | 2026-09-07 | 27
6  | 게시글 06 | 2026-09-06 | 27
5  | 게시글 05 | 2026-09-05 | 27
4  | 게시글 04 | 2026-09-04 | 27
3  | 게시글 03 | 2026-09-03 | 27
2  | 게시글 02 | 2026-09-02 | 27
1  | 게시글 01 | 2026-09-01 | 27
(7행)

마지막 페이지는 10 행이 안 되는 7 행이 돌아옵니다. 페이지 1 의 끝 18번(17일)과 페이지 2 의 시작 17번(17일), 페이지 2 의 끝 8번(7일)과 페이지 3 의 시작 7번(7일)이 id 내림차순으로 이어져 겹치거나 빠진 행이 없습니다.

예제 4: 범위를 벗어난 페이지

sql
SELECT
       id
     , title
     , COUNT(*) OVER () AS total
  FROM board
 ORDER BY created_at DESC, id DESC
OFFSET 30 ROWS FETCH NEXT 10 ROWS ONLY;
text
ID | TITLE | TOTAL
---+-------+------
(0행)

27 행을 넘어선 곳부터 읽으면 0행이라 total 도 받을 수 없습니다. 사용자가 주소창에 큰 페이지 번호를 넣거나, 삭제로 마지막 페이지가 사라진 경우입니다. 총 건수를 이 쿼리에서만 얻으면 그 경우에 "총 0건" 으로 잘못 보이므로, 서버에서 페이지 번호를 총 페이지 수로 자르거나 총 건수를 별도 COUNT(*) 로 받습니다.

팁

총 건수 COUNT 는 대용량 표에서 비싼 연산입니다. 화면에 "총 3,201,554건" 이 꼭 필요하지 않다면 "더 보기" 버튼으로 다음 페이지가 있는지(FETCH 를 한 건 더 요청) 만 확인하거나 근사치를 씁니다.

예제 직접 실행

아래 폴더의 SQL 파일을 Git Bash 에서 H2 메모리 DB 로 실행합니다. 방언은 파일 안의 SET MODE 로 바꿉니다.

cd sql-src/work_01_paging
ls *.sql
bash ../run.sh <파일>.sql
코드 예제
  • 예제 1: 정렬 동점 확인
  • 예제 2: 페이지 1 과 총 건수
  • 예제 3: 페이지 3
  • 예제 4: 범위를 벗어난 페이지
이전 섹션2 핵심 원리3 / 6다음 섹션4 응용 변형 예제