Chapter 12 오라클 데이터베이스 페이징 처리 - oneso123456789/2022 GitHub Wiki

Feedback

내용 정리가 너무 길고 난잡함 더 추려서 핵심적인 내용만 뽑아야함

Chapter 12 오라클 데이터베이스 페이징 처리

구현된 기능들 중 가장 미숙한 부분은 목록 페이지 부분임
목록 페이지는 기본적으로 페이징(pagination) 처리가 필요한데 상식적으로
생각해 봐도 수많은 데이터를 한 페이지에서 보여주면, 처리 성능에 영향을 미치게됨
또한, 브라우저에서도 역시 데이터의 양이나 처리 속도에 문제를 일으키게 됨

일반적으로 페이징 처리는 크게 번호를 이용하거나 계속보기의 형태로 구현함
번호를 이용한 페이징 처리는 과거 웹 초기부터 이어오던 방식이고,
계속보기는 Ajax와 앱이 등장한 이후에 무한 스크롤이나 더 보기와 같은 형태로 구현됨
예제에서 목록 페이지는 전통적인 번호를 이용하는 방식으로 처리함

오라클에서 페이징 처리하는 것은 MySQL에 비해서 추가적인 지식이 필요하므로 이에 대한 학습을 선행해야함

12.1 order by의 문제

프로그램을 이용해서 정렬을 해 본 적이 있다면 데이터의 양이 많을수록 정렬이라는 작업이 얼마나 많은 리소스를 소모하는지 알 수 있음
데이터베이스는 경우에 따라서 수백만 혹은 천 만개 이상의 데이터를 처리하기 때문에
이 경우 정렬을 하게 되면 엄청나게 많은 시간과 리소스를 소모하게 됨

데이터베이스를 이용할 때 웹이나 애플리케이션에 가장 신경 쓰는 부분은

  1. 빠르게 처리 되는것
  2. 필요한 양만큼만 데이터를 가져오는 것임

예를 들어, 거의 모든 웹페이지에서 페이징을 하는 이유는 최소한의 필요한 데이터만을
가져와서 빠르게 화면을 보여주기 위함임

만일 수백 만개의 데이터를 매번 정렬을 해야 하는 상황이라면 사용자는 정렬된 결과를 볼 때까지 오랜 시간을 기다려야만하고,
특히 웹에서 동시에 여러 명의 사용자가 정렬이 필요한 데이터를 요청하게 된다면
시스템에는 많은 부하가 걸리게 되고
연결 가능한 커넥션의 개수가 점점 줄어서 서비스가 멈추는 상황을 초래하게 됨

// 2022_04_15 오전 1시 53분 269p 여기까지함

빠르게 동작하는 SQL을 위해서는 먼저 order by를 이용하는 작업은 가능하면 하지 말아야 함
order by는 데이터가 많은 경우에 엄청난 성능의 저하를 가져오기 때문에

  1. 데이터가 적은경우와
  2. 정렬을 빠르게 할 수 있는 방법이 있는 경우가 아니라면 order by는 주의해야만 함

12.1.1 실행 계획과 order by

오라클의 페이징 처리를 제대로 이해하기 위해서 반드시 알아두어야 하는 것이 실행 계획(execuion plan)임
실행계획은 말 그대로 SQL을 데이터베이스에서 어떻게 처리할 것인가?에 대한 것이며
SQL이 데이터베이스에 전달되면 데이터베이스는 여러 단계를 거쳐서 해당 SQL을 어떤 순서와 방식으로 처리할 것이지 계획을 세우게 됨

데이터베이스에 전달된 SQL문은 아래와 같은 과정을 거쳐서 처리됨
SQL 파싱 -> SQL 최적화 -> SQL 실행

SQL 파싱 단계에서는 SQL 구문에 오류가 있는지 SQL을 실행해야 하는 대상 객체(테이블, 제약 조건, 권한 등)가 존재하는지를 검사함

SQL 최적화 단계에서는 SQL이 실행되는데 필요한 비용(cost)을 계산하게 됨
이 계산된 값을 기초로 해서 어떤 방식으로 실행하는 것이 가장 좋다는것을 판단하는
실행 계획(execuion plan)을 세우게 됨

SQL 실행 단계에서는 세워진 실행 계획을 통해서 메모리상에서 데이터를 읽거나 물리적인 공간에서 데이터를 로딩하는 등의 작업을 하게 됨
개발자들은 도구를 이용하거나 SQL Plus 등을 이용해서 특정한 SQL에 대한 실행 계획을 알아볼 수 있음
SQL Developer에서는 간단히 버튼을 클릭해서 실행 계획을 확인할 수 있음

예를 들어, 게시물 번호의 역순으로 출력하라는 처리를 한다면 SQL Developer에서 다음과 같이 처리할 수 있음 select * from tbl_board order by bno desc;(p270참고)
상단의 버튼 중에는 SQL에 대해서 실행 계획을 쉽게 볼 수 있도록 버튼이 제공됨

실행 계획을 보면 트리 구조로 방금 전 실행한 SQL이 어떻게 처리된 것인지를 알려줌
흔히 SQL 튜닝이라고 하는 작업은 이를 보고 어떤 방식이 더 효과적인지를 판단해서 수정하게 됨

이 책에서는 실행 계획에 대해서 많은 내용을 설명할 수는 없지만,
가장 간단하게 실행 계획을 보는 방법은**안쪽에서 바깥쪽으로, 위에서 아래로**봐주면됨

p270의 그림의 내용을 해석하자면 TBL_BOARD테이블을 FULL로 접근하고 정렬했다는 것을 의미함
FULL이라는 의미는 테이블 내의 모든 데이터를 스캔(scan)했다는 의미임
실행 계획을 세우는 것은 데이터베이스에서 하는 역활이기 때문에 데이터의 양이나 제약 조건등의
여러 상황에 따라서 데이터베이스는 실행 계획을 다르게 작성함

테스트를 위해서 데이터가 좀 많아지도록 아래의 SQL을 여러 번 실행해서 데이터를 수 백 만개로 만든 후에 커밋을 함

-- 재귀 복사를 통해서 데이터의 개수를 늘린다, 반복해서 여러번 실행
insert into tbl_board (bno, title, content, writer)
(select seq_board.nextval, title, content, writer from tbl_board);

위의 insert문을 여러 번 실행하게 되면 현재 tbl_board 테이블의 데이터 수만큼 다시 insert가 진행됨
결과를 보면 insert문을 실행할 때마다 2배씩 데이터가 늘어나게 됨

commit 후에 select count(*) from tbl_board를 실행해 보면 데이터 수가 엄청나게 늘어난 것을 확인할 수 있음 대충 300만건정도를 넣으라고 했지만 나는 20만건 정도만 넣어줌

데이터가 많아지면 정렬에 그만큼의 시간을 소모하게 됨
고의적으로 bno라는 칼럼의 값에다 1을 추가한 값을 역순으로 정렬하는 SQL을 만든다면 다음과 같음
select * from tbl_board order by bno + 1 desc;

연산 작업이 추가되기는 했지만 SQL문의 결과가 정도가 걸려서 나오는데 얼마 차이가 없었음
아마도 나는 반복적으로 몇 번 실행해서 데이터베이스가 메모리상에 보관하는 데이터를 가져와서 그런거 같음
db에 새로 접속해서 실행해보니 3.2초 정도가 걸림

위의 SQL을 실행한 결과는 테이블 전체를 스캔(조사)하는 것을 볼 수 있음

실행 계획을 잠깐 살펴보면 TBL_BOARD를 FULL로 조사(스캔)했고, 바깥쪽으로 가면서 SORT가 일어난 것을 볼 수 있음 이때 가장 많은 시간을 소모하는 작업은 정렬 하는 작업임

위의 SQL에서 order by bno + 1 dese라는 조건에서 +1을 하는 것은 정렬에 아무런 도음을 주지 않으므로
아래와 같이 SQL을 수정해서 실행함
select * from tbl_board order by bno desc;

연산이라는 차이가 있기는 하지만 실행에 걸리는 시간은 차이가 많이 나게됨

이전엔 3.2초지만 지금은 0초대에 실행됨
하지만 책에선 실행 계획도 기존과 다르게 작동한다고 했는대 나는 sql문이 달라도 똑같음
위의 sql문에선 pk_board를 사용하지 않았지만 책내용에선 사용한 것으로 보아 책 내용이 꺽여있는거 같음

기존의 SQL이 TBL_BOARD 테이블 전체를 스캔했지만, 이번에는 PK_BOARD라는 것을 이용해서 접근하고
기존과 달리 맨 위의 SORT과정이 없는것을 볼 수 있음
이라고 써있는대 나는 SORT과정을 볼수있음 ..

뒤의 내용인 데이터 베이스의 인덱스(index)에 대해서 배우면 해결될 것으로 보임

12.2 order by 보다는 인덱스

데이터가 많은 상태에서 정렬 작업이 문제가 된다는 사실을 알았다면,
이 문제를 어떻게 해결해야 하는지를 살펴봐야함
가장 일반적인 해결책은 인덱스(index)를 이용해서 정렬을 생략하는 방법
결론부터 말하자면 인덱스라는 존재가 이미 정렬된 구조이므로 이를 이용해서 별도의 정렬을 하지 않는 방법임

인덱스가 무엇인지에 대해서는 조금 더 뒤에서 살펴볼 예정이고 ,
우선 위와 같은 상황에서 다음과 같은 SQL을 실행해 보겠음

select 
    /*+ INDEX_DESC(tbl_board pk_board)  */
    *
    from 
        tbl_board
    where bno > 0;

위의 SQL을 실행한 결과는 테이블 전체를 조사하고 정렬한 것과 결과는 동일하지만
실행 시간은 엄청나게 차이가 나게 됨
가장 중요한 점은 SQL의 실행 시간이 거의 0초(혹은 0.1)로 나온다는 점임
SQL문의 실행 계획은 P274 그림과 같은 모습을 가지게됨

SQL의 실행 계획에서 주의해서 봐야 하는 부분은

  1. SORT를 하지 않았다는 점
  2. TBL_BOARD를 바로 접근하는 것이 아니라 PK_BOARD를 이용해서 접근한점
  3. RANGE SCAN DESCENDING, BY INDEX ROWID로 접근했다는점

12.2.1 PK_BOARD라는 인덱스

tbl_board 테이블을 생성했을 때의 SQL을 다시 한 번 살펴보겠음

create TABLE tbl_board(
bno NUMBER(10,0),
title VARCHAR2(200) not null,
content varchar2(2000) not null,
writer varchar2(50) not NULL,
regdate date DEFAULT SYSDATE,
updatedate date DEFAULT SYSDATE
);

alter table tbl_board add CONSTRAINT pk_board
PRIMARY key(bno);

데이터베이스의 PK

테이블을 생성할 때 제약조건으로 PK를 지정하고 PK의 이름이 pk_board라고 지정하였음
데이터베이스에서 PK는 상당히 중요한 의미를 가지는데, 흔히 말하는 식별자의 의미와 인덱스의 의미를 가짐

// 2022-04-16 오후 9시 07분 여기까지

인덱스는 말 그대로 색인

우리가 가장 흔히 접하는 인덱스는 도서 뒤쪽에 정리되어 있는 색인임
색인을 이용하면 사용자들은 테이블 전체를 살펴볼 필요 없이 색인을 통해서 자신이
원하는 내용이 책의 어디에 있는지 알 수 있음
색인은 사람들이 쉽게 찾아볼 수 있게 알파벳 순서나 한글 순서로 정렬함

데이터베이스에서 인덱스를 이해하는 가장 쉬운 방법

데이터베이스의 테이블을 하나의 책이라고 생각하고 어떻게 데이터를 찾거나 정렬하는지 생각하는것

스캔(scan)

원하는 내용을 위에서 부터 혹은 반대로 찾아나가는것

PK를 부여하는 이유

데이터베이스에 테이블을 만들 때 PK를 부여하며 지금까지 얘기한 인덱스라는 것이 만들어짐
DB를 만들 때 PK를 지정하는 이유는 식별이라는 의미도 있지만,
구조상으로는 인덱스라는 존재(객체)가 만들어지는 것을 의미함
tbl_board 테이블은 bno라는 칼럼을 기준으로 인덱스를 생성함


인덱스 그림표현(P276)

식별키로 만들어진 인덱스를 보면 bno 값이 순서대로 정렬된 것을 볼 수 있음
그러나 테이블 모습을 보면 마치 책장에 책을 막 넣은 것처럼 중간에 순서가 섞여 있는 경우가 대부분임
이때 인덱스와 실제 테이블을 연결하는 ROWID라는 고리가 존재함


ROWID

데이터베이스내의 주소에 해당하는데 모든 테이터는 자신만의 주소를 가지고 있음
그 값이 바로 ROWID임

SQL에서 데이터를 찾는방법

SQL을 통해서 bno 값이 100번인 데이터를 찾고자 할 때에는 SQL은 where bno = 100
같은 조건을 주게 됨
이를 처리하는 데이터베이스 입장에서는 tbl_board라는 책에서 bno 값이 100인
데이터를 찾아야함

FULL SCAN

이때 만일 책이 얇아서 내용이 많지 않다면 속히 전체를 살펴보는 것이 더 빠를것이고,
(이를 데이터베이스 쪽에선 FULL SCAN이라고 표현)

하지만 내용이 많고, 색인이 존재한다면 당연히 색인을 찾고 색인에서 주소를 찾아서
접근하는 방식을 이용할 것임


실행 계획을 보면 이러한 생각이 데이터베이스 내에서 진행되는 것을 확인 가능함
(p 277 or sql developer 참고)
안쪽을 먼저 보면 PK_BOARD는 인덱스이므로 먼저 인덱스를 이용해서 100번 데이터가
어디에 있는지 ROWID를 찾아내고, 바깥쪽을 보면 BY INDEX ROWID라고 되어 있는 말
그대로 ROWID를 통해서 테이블에 접근함


12.3 인덱스를 이용하는 정렬

인덱스에서 가장 중요한 개념 중 하나는 정렬이 되어 있다는 점
정렬이 되어 있는 상태이므로 데이터를 찾아내서 이들을 SORT 하는 과정을 생략 가능함

만일 bno의 역순으로 정렬한 결과를 원한다면 이미 정렬된 인덱스를 이용해서
뒤에서부터 찾아올라가는 방식을 이용할 수 있음

DESCENDING

뒤에서부터 찾아서 올라간다는 개념임

이전에 실행한 bno의 역순으로 데이터를 가져올 때의 실행 계획을 살펴보면
PK_BOARD라는 인덱스를 이용하는데 DESCENDING을 하고 있는 것을 볼 수 있음

역순정렬

사용자가 역순으로 정렬을 원한다면
가장 먼저 찾은 bno값은 가장 큰 값을 가진 데이터가 됨 이후에는 테이블에 접근해서
데이터를 가져오게됨, 이런 과정이 반복되면 정렬을 하지 않아도 동일하게
정렬된 결과를 얻을 수 있음

정순정렬

사용자가 bno의 순서대로 정렬해 달라 요구하면
PK_BOARD 인덱스가 앞에서부터 찾아서 내려가는 주고를 이용하는 것이 효율적임

select * from tbl_board order by bno;

위 SQL문은 실행 계획상으로 PK_BOARD 인덱스에 먼저 접근하고, TBL_BOARD를
이용하는 것을 볼 수 있음
SORT가 없기 때문에 0초에 가까운 성능을 보여줌
실무에서도 데이터의 양이 많고 정렬이 필요한 상황이라면 우선적으로 생각하는 것이
인덱스를 작성하는 것임
데이터의 양이 수천, 수만개 정도의 정렬은 그다지 부하가 걸리지 않음,
하지만 그 이상의 데이터를 처리해야 하는 상황이라면 정렬을 안할 수 있는 방법을 고민해야함

아래의 코드는 index를 이용했지만 계획설명의 cost는 똑같았음

select 
    /*+ INDEX_asc(tbl_board pk_board)  */
    *
    from 
        tbl_board
    where bno > 0;



12.3.1 인덱스와 오라클 힌트(hint)

웹페이지의 목록은 주로 시간의 역순으로 정렬된 결과를 보여줌
최신 데이터가 가장 중요하기 때문에 시간의 역순 정렬해서 최신 게시물들을 보여주게됨

이 경우 개발자의 입장에서는 정렬을 안 하는 방식으로 select문을 실행하고 싶어함

오라클은 select문을 전달할 때 힌트(hint)라는 것을 사용할 수 있음

힌트(hint)

데이터베이스에 지금 내가 전달한 select문을 이렇게 실행해 주면 좋겠다라는 힌트임
또한 특이하게도 select문을 어떻게 처리하는지에 대한 얘기일 뿐이므로
힌트 구문에서 에러가 나도 전혀 SQL 실행에 지장을 주지 않음
따라서 힌트를 이용한 select문을 작성한 후에는 실행 계획을 통해서
개발자가 원하는대로 SQL이 실행되는지를 확인하는 습관을 가져야함

게시물 목록은 반드시 시간의 역순으로 나와야 하기 때문에
SQL에서는 order by bno desc와 같은 조건은 데이터베이스 상황에 따라서 테이블의
모든 데이터를 정렬하는 방식으로 동작할 수 있다는 점임

반면에 힌트는 개발자가 데이터베이스에 어떤 방식으로 실행해 줘야 하는지를 명시함
따라서 조금 강제성이 부여되는 방식임

select * from tbl_board order by bno desc;

select /*+INDEX_DESC (tbl_board pk_board) */*
from tbl_board;

위의 두 SQL은 동일한 결과를 생성하는 SQL임


두번째 select문은 order by 조건이 없어도 동일한 결과가 나온 것에 주목해야함
select문에서 힌트를 부여했는데 힌트의 내용이 tbl_board 테이블에 pk_board 인덱스를 역순으로 이용해 줄것
이므로 실행 계획에서 이를 활용하고있는 것을 확인할 수 있음

오라클 데이터베이스에서 사용할 수 있는 힌트는 여러 종류가 있음
하지만 이에 대한 모든 내용을 설명하는 것은 너무 방대하기 때문에 자주 사용하는 몇가지만 정리하겠음


12.3.2 힌트 사용 문법

select문을 작성할 때 힌트는 잘못 작성되어도 실행할 때는 무시되기만 하고 별도의 에러는 발생하지 않음

힌트의 문법

SELECT
 /*+ Hint name(param) */ columm name,...
FROM
 table name

힌트 구문은 위의 예처럼/*+로 시작하고 */로 마무리됨
힌트 자체는 SQL로 처리되지 않기 때문에 위의 그림처럼 뒤에 칼럼명이 나오더라도 별도의 ,로 처리되지 않음

12.3.3 FULL 힌트

FULL 힌트란 select 문을 실행할 때 테이블 전체를 스캔할 것으로 명시하는 힌트를 말함
FULL 힌트는 테이블의 모든 데이터를 스캔하기 때문에 데이터가 많을 때는 상당히 느리게 실행됨

예를 들어서 tbl_board 테이블을 FULL 스캔하도록하고,
이 상태에서 정렬을 하려면 다음과 같이 작성할 수 있음

select /*+ FULL(tbl_board) */ * from tbl_board order by bno desc;

해당 SQL의 실행 계획을 보면 TBL_BOARD를 FULL로 접근하고, 다시 SORT가 적용된 것을 볼 수 있음
실행 시간은 모르겠지만 3657 COST를 소비함 위에 인덱스를 사용한 SQL문이 2264 COST를 소비한것에 비해서 매우 비효율적임
따라서 실행속도가 현저히 느리고다 판단할수 있음


12.3.4 INDEX_ASC, INDEX_DESC 힌트

흔히 목록페이지에서 가장 많이 사용하는 힌트는 인덱스와 관련된
INDEX_ASC,INDEX_DESC힌트임
이 힌트들은 ASC/DESC에서 알 수 있듯이 인덱스를 순서대로 이용할 것인지 역순으로 이용할 것인지를 지정하는것임

INDEX_ASC/DESC 힌트는 주로 order by를 위해서 사용한다고 생각하면됨
인덱스 자체가 정렬을 해 둔 상태이므로 이를 통해서 SORT 과정을
생략하기 위한 용도임

INDEX_ASC/DESC 힌트는 테이블 이름과 인덱스 이름을 같이 파라미터로 사용함

select /*+ INDEX_ASC(tbl_board pk_board) */ * from tbl_board
where bno > 0;

INDEX_ASC,DESC를 이용하는 경우에는 동일한 조건의 order by 구문을 작성하지 않아도 됨
예를 들어, 위의 SQL문에서 아무런 order by 조건이 없어도 bno의 순번을 통해서
접근하기 때문에 order by bno asc구문은 필요가 없음

12.4 ROWNUM과 인라인뷰

페이징 처리를 위해서 역순으로 게시물의 목록을 조회하는 작업이 성공했다면,
이제는 전체가 아닌 필요한 만큼의 데이터를 가져오는 방식에 대해서 알아야함
(오늘 생긴 오류도 전체 목록의 데이터 20만건을 list페이지로 가져오려다가 생김)

오라클 db는 페이지 처리를 위해서 ROWNUM이라는 특별한 키워드를 사용해서 데이터에 순번을 붙여서 사용함

ROWNUM이란 한마디로 SQL이 실행된 결과에 넘버링을 해준다고 생각하면됨

ROWNUM은 실제 데이터가 아니라 테이블에서 데이터를 추출한 후에 처리되는 변수 이므로 상황에 따라서 그 값이 매번 달라질 수 있음

우선 아무 조건을 적용하지 않고 tbl_board 테이블에 접근하고 각 데이터에 ROWNUM을 적용하면
SQL에 아무 조건이 없기 때문에 데이터는 테이블에 섞여 있는 상태 그대로 나옴

다음으로 FULL힌트를 사용한 SQL을 조회하고 다시 정렬한 방식임

select /* + FULL(tbl_board)*/
    rownum rn , bno, title
from tbl_board where bno > 0
order by bno;

결과를 보면 역시 ROWNUM과 상관없이 정렬과정에서 뒤로 밀리는것을 볼 수 있음

이를 통해서 알 수 있는 사실은 ROWNUM이라는 것은 데이터를 가져올때 적용되는것이고,
이 후에 정렬 과정에서는 ROWNUM이 변경되지 않는다는 점임
다른말로는 정렬은 나중에 처리된다는 뜻도 됨

12.4.1 인덱스를 이용한 접근 시 ROWNUM

ROWNUM의 의미가 테이블에서 데이터를 가져오면서 붙는 번호라는 사실을 기억해보면
결국 문제는 테이블에 어떤 순서로 접근하는가에 따라서 ROWNUM 값은 바뀔 수 있다는 뜻이 됨

만일 PK_BOARD 인덱스를 통해서 접근한다면 다음과 같은 과정으로 접근됨

  1. PK_BOARD 인덱스를 통해서 테이블에 접근
  2. 접근한 데이터에 ROWNUM 부여 다음과 같은 조건의 SQL문은 아래와 같으며
SELECT /*+ INDEX_ASC(tbl_board pk_board)*/
    ROWNUM rn ,bno, title, content
from tbl_board;

SQL문은 힌트를 이용해서 tbl_board 테이블을 pk_board의 순번으로 접근함
따라서 가장 먼저 찾은 데이터부터 ROWNUM 1을 부여함

이때 만약 역순으로 테이블에 접근한다면 반대로 bno 값이 가장 큰 값에 ROWNUM 1을 부여해줌 SQL문은 다음과 같음

SELECT /*+ INDEX_DESC(tbl_board pk_board)*/
    ROWNUM rn ,bno, title, content
from tbl_board
where bno > 0;

굳이 차이점을 나열하자면 index를 desc로 정렬하고 조건문으로 bno 값이 0보다 큰 칼럼만 찾아옴
이 방식을 이용하면 각 게시물을 정렬하면서 순번을 매겨줄 수 있음
1페이지의 경우 RN이라는 칼럼의 값이 1부터 10에 해당한다고 볼 수 있음(10개씩 페이징을 한다는 전제로)

12.4.2 페이지 번호 1, 2의 데이터

한 페이지당 10개의 데이터를 출력한다고 가정하면 ROWNUM 조건을 WHERE구문에
추가해서 작성함

SELECT /*+ INDEX_DESC(tbl_board pk_board)*/
    ROWNUM rn ,bno, title, content
from tbl_board
where rownum <= 10;

SQL 실행 결과는 가장 높은 번호의 게시물 10개만 출력되는 것을 볼 수 있음
이때 실행 계획을 통해서 PK_BOARD 인덱스를 역순으로 접근하는 것을 확인가능
WHERE조건에서 특이하게 ROWNUM 조건은 테이블을 접근할 때 필터링이 적용되는 것을 볼 수 있음

일반적으로 1페이지 데이터를 구했다면 흔히 동일한 방식으로 2페이지 데이터를 구할 수 있다고 생각함
하지만 동일한 방식으로는 절대 원하는 결과를 얻을 수 없음

SELECT /*+ INDEX_DESC(tbl_board pk_board)*/
    ROWNUM rn ,bno, title, content
from tbl_board
where rownum > 10 and rownum <= 20;

위의 SQL문을 보면 rownum이 10보다 크고 20보다 작거나 같은 데이터들을 가져온다고
생각하지만 실제로는 어떤 결과도 나오지 않음
이유를 알기위해서 실행계획을 살펴보면
우선ㄴ ROWNUM > 10 조건에 맞는 데이터를 찾게됨
여기서 문제가 생김 TBL_BOARD에 처음으로 나오는 ROWNUM 값이 1이라는 것임
TBL_BOARD에서 데이터를 찾고 ROWNUM 값이 1이 된 데이터는 where 조건에 의해서 무효화됨

이후에 다시 다른 데이터를 가져오면 새로운 데이터가 첫번째가 되므로 다시 ROWNUM은 1이됨
따라서 ROWNUM값은 항상 1로 만들어지고 없어지는 과정이 반복되므로
테이블의 모든 데이터를 찾아내지만 결과는 아무것도 나오지 않음

이러한 이유로 SQL문을 작성할 때 ROWNUM 조건은 반드시 1이 포함되어야함
수정된 SQL문은 다음과 같음

-- ROWNUM은 반드시 1이 포함되도록 해야 한다.
SELECT /*+ INDEX_DESC(tbl_board pk_board)*/
    ROWNUM rn ,bno, title, content
from tbl_board
where rownum <= 20;

결과는 역순으로 데이터를 20개 가져옴
정리하자면 ROWNUM 만으로는 10페이지씩 순차적으로 페이징 처리할수없어
다른 기능을 같이 사용해야 할꺼 같음

12.4.3 인라인뷰(In-line View)처리

이전에 10개씩 목록을 출력하는 경우 2페이지의 데이터는 20개를 가져오는 데는 성공함,
하지만 1페이지의 내용이 같이 출력되는 문제가 있음
이 문제를 해결하기 위해 인라인뷰를 사용함

인라인뷰는 쉽게 말해서 SELECT문 안쪽 FROM에 다시 SELECT문으로 이해 가능함
논리적으론 어떤 결과를 구하는 SELECT문이 있고, 그 결과를 다시 대상으로
SELECT를 하는 것임

데이터베이스에서는 테이블이나 인덱스와 같이 뷰(View)라는 개념이 존재함

뷰는 일종의 창문같은 개념으로 복잡한 SELECT 처리를 하나의 뷰로 생성하고,
사용자들은 뷰를 통해서 복잡하게 만들어진 결과를 마치
하나의 테이블처럼 쉽게 조회하는 개념임

인라인뷰란 이러한 뷰의 작성을 별도로 하지 않고 말 그대로 FROM 구문 안에 바로 작성함

SELECT ...
FROM(
     SELECT ...
      FROM ...
    )

이런 형태로 작성함

따라서 인라인뷰를 적용한 2페이지 데이터의 처리는 다음과 같음

SELECT
    bno,
    title,
    content
FROM
    (
        SELECT /*+ INDEX_DESC(tbl_board pk_board)*/
            ROWNUM rn,
            bno,
            title,
            content
        FROM
            tbl_board
        WHERE
            ROWNUM <= 20
    )
WHERE
    rn > 10;

기존의 SQL과 비교해 보면 20개의 데이터를 가져온후 2페이지에 해당하는 10개만
추출하는 방식으로 구현됨

이 과정을 정리하면

  • 필요한 순서로 정렬된 데이터를 ROWNUM을 붙인다.
  • 처음부터 해당 페이지의 데이터를 ROWNUM <= 30과 같은 조건을 이용해서 구한다.
  • 구해놓은 데이터를 하나의 테이블처럼 간주하고 인라인뷰로 처리한다.
  • 인라인뷰에서 필요한 데이터만을 남긴다.
⚠️ **GitHub.com Fallback** ⚠️