티스토리 뷰
구현된 기능들 중 가장 미숙한 부분은 목록 페이지이다.목록 페이지는 기본적으로 페이징처리가 필요한데 상식적으로 생각해 봐도 수많은 데이터를 한 페이지에서 보여주면 처리 성능에 영향을 미친다.또한 브라우저에서도 역시 데이터의 양이나 처리 속도에 문제를 일으키게 된다.
일반적으로 페이징 처리는 크게 번호를 이용하거나 계속 보기의 형태로 구현된다.
번호를 이용한 페이징 처리는 과거 웹 초기부터 이어오던 방식이고,계속보기는 Ajax와 앱이 등장한 이우헤 무한 스크롤이나 더보기와 같은 형태로 구현된다.
오라클에서 페이징 처리하는 것은 MySQL에 비해서 추가적인 지식이 필요하다.
oerder by의 문제
프로그램을 이용해서 정렬을 해 본적이 있다면 데이터의 양이 많을수록 정렬이라는 작업이 얼마나 많은 리소스를 소모하는지 알 수 있다.데이터베이스는 경우에 따라서 수백만 혹은 천 만개 이상의 데이터를 처리하기 때문에 이 경우 정렬을 하게 되면 엄청나게 많은 시간과 리소스를 소모하게 된다.
데이터베이스를 이용할 때 웹이나 애플리케이션에 가장 신경 쓰는 부분은
- 빠르게 처리되는 것
- 필요한 양만큼 데이터 가져오기
예를 들어 거의 모든 웹페이지에서 페이징을 하는 이유는 최소한의 필요한 데이터만을 가져와서 빠르게 화면에 보여주기 위함이다.
만일 수백 만개의 데이터를 매번 정렬을 해야 하는 상황이라면 사용자는 정렬된 결과를 볼 때까지 오랜 시간을 기다려야만 하고 특히 웹에서 동시에 여러 명의 사용자가 정렬이 필요한 데이터를 요청하게 된다면 시스템에는 많은 부하가 걸리게 되고 연결 가능한 커텍션의 개수가 점점 줄어서 서비스가 멈추는 상황을 초래하게 된다.
빠르게 동작하는 SQL을 위해서는 먼저 order by를 이용하는 작업을 가능하면 하지 말아야 한다.
order by는 데이터가 많은 경우에 엄청난 성능의 저하를 가져오기 때문에
- 데이터가 적은 경우
- 정렬을 빠르게 할 수 있는 방법이 있는 경우가 아닐때
order by는 주의해야한다.
실행 계획과 order by
오라클의 페이징 처리를 제대로 이해하기 위해서 반드시 알아두어야 하는 것이 실행 계획(execution plan)이다.
실행 계획은 말 그대로 SQL을 데이터베이스에서 어떻게 처리할 것인가에 대한 것이다.
SQL이 데이터베이스에 전달되면 데이터베이스는 여러 단계를 거쳐서 해당 SQL을 어떤 순서와 방식으로 처리할 것인지 계획을 세우게 된다.
데이터베이스에 전달된 SQL문은 다음과 같이 처리된다.

SQL 파싱단계에서는 SQL 구문에 오류가 있는지 SQL을 실행해야 하는 대상 객체(테이블,제약 조건,권한 등)가 존재하는지를 검사한다.SQL 최적화 단계에서는 SQL이 싱행되는데 피룡한 비용을 계산하게 된다.
이 계산된 값을 기초로 해서 어떤 방식으로 실행하는 것이 가장 좋다는 판단하는 실행 계획(execution plan)을 세우게 된다.
SQL 실행 단계에서는 세워진 실행 계획을 통해서 메모리상에서 데이터를 읽거나 물리적인 공간에서 데이터를 로딩하는 등의 작업을 하게 된다.
개발자들은 도구를 이용하거나 SQL Plus 등을 이용해서 특정한 SQL에 대한 실행 계획을 알아볼 수 있다.
SQL Developer에서는 간단히 버튼을 클릭해서 실행 계획을 확인할 수 있다.
예를들어 게시물 번호의 역순으로 출력하라는 쿼리를 실행한다면 다음과 같이 실행할 수 있다.
select * from tbl_board order by bno desc;

상단의 버튼 중에서 SQL에 대해서 실행 계획을 쉽게 볼 수 있는 버튼이 있다.

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

가장 간단하게 실행 계획을 보는 방법은 안쪽에서 바깥쪽으로,위에서 아래 봐주면 된다.
위 사진을 해석하자면
tbl_board 테이블을 FULL로 접근하고 정렬했다는 것을 의미한다.
FULL이라는 의미는 테이블 내의 모든 데이터를 스캔했다는 의미이다.
실행 계획을 세우는 것은 데이터베이스에서 하는 역할이기 때문에 데이터의 양이나 제약 조건 등의 여러 상황에 따라서 데이터베이스는 실행 계획을 다르게 작성한다.
테스트를 위해서 데이터가 좀 많아지도록 아래의 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를 실행해 보면 데이터의 수가 엄청나게 늘어난 것을 볼 수 있다.
데이터가 많아지면 정렬에 그만큼의 시간을 소모하게 된다.고의적으로 bno라는 칼럼의 값에다 1을 추가한 값을 역순으로 정렬하는 SQL을 만든다면 다음과 같다.
select * from tbl_board order by bno+1 desc;
연산 작업이 추가되기는 했지만 SQL문의 결과가 나오는데는 데이터 생성전보다 증가했다.
또한 테이블 전체를 스캔하는 것을 볼 수 있다.
실행 계획을 살펴보면 tbl_board를 FULL로 스캔하였고,상단을 보면 SORT 가 일어난 것을 볼 수 있다.

이때 가장 많은 시간을 소모하는 작업은 정렬하는 작업이다.
위의 SQL에서 order by bno+1 desc라는 조건에서 +1을 하는 것은 정렬에 아무런 도움을 주지 않으므로 아래와 같이 SQL을 수정해서 실행한다.
select * from tbl_board;
연산이라는 차이가 있기는 하지만 실행에 걸리는 시간은 차이가 많이 나게 된다.

이 결과의 차이에는 실행 계획도 기존과 다르게 동작한다.
기존의 SQL이 tbl_board 테이블 전체를 스캔햇지만 이번에는 pk_board라는 것을 이용해서 접근하고 기존과 달리 맨 위의 sort과정이 없는 것을 볼 수 있다.
(sort의 유무차이가 없다면 컴퓨터 재부팅을 추천한다.또한 데이터의 양이 200~300만개 정도가 되야지 sort 유무의 차이가 있다.)
order by 보다는 인덱스
데이터가 많은 상태에서 정렬 작업이 문제가 된다는 사실을 알았다면 이문제를 어떻게 해결해야 하는지 알아보겠다.
가장 일반적인 해결책은 인덱스를 이용해서 정렬을 생략하는 방법이다.결론부터 말하자면 인덱스라는 존재가 이미 정렬된 구조이므로 이를 이용해서 별도의 정렬을 하지 않는 방법이다.
select * from tbl_board where bno>0;
위의 SQL을 실행한 결과는 테이블 전체를 조사하고 정렬한 것과 결과는 동일하다.
가장 중요한 점은 SQL의 실행 시간이 거의 0초(혹은 0.1초 이하)로 나온다는 점이다.
SQL문의 실행 계획은 아래와 같다.

SQL의 실행 계획에서 주의해서 봐야 하는 부분은
- sort를 하지 않았다는 점
- tbl_board를 바로 접근하는 것이 아니라 pk_board를 이용해서 접근한 점
- range scan descending,by index rowid로 접근했다는 점
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_board라고 지정하였다.
데이터베이스에서 PK는 상당히 중요한 의미를 가지는데 흔히 말하는 식별자의 의미와 인덱스의 의미를 가진다.
인덱스는 말 그대로 색인이다.우리가 가장 흔히 접하는 인덱스는 도서 뒤쪽에 정리되어 있는 색인이다.
색인을 이용하면 사용자들은 책 전체를 살펴볼 필요 없이 색인을 통해서 자신이 원하는 내용이 책의 어디에 있는지 알 수 있다.데이터베이스에서 인덱스를 이해하는 가장 쉬운 방법은 데이터베이스의 테이블을 하나의 책이라 생각하고 어떻게 데이터를 찾거나 정렬하는지를 생각하는 것이다.색인은 사람들이 쉽게 찾아볼 수 있게 알파벳 순서나 한글 순서를 정렬한다.이를 통해서 원하는 내용을 위에서부터 혹은 반대로 찾아나가는데 이를 스캔이라고 표현한다.
데이터베이스에 테이블을 만들 때 PK를 부여하면 지금까지 얘기한 인덱스라는 것이 만들어진다.
데이터베이스를 만들때 PK를 지정하는 이유는 식별이라는 의미가 있지만 구조상으로는 인덱스라는 존재(객체)가 만들어지는 것을 의미한다.
tbl_board 테이블은 bno라는 칼럼을 기준으로 인덱스를 생성하게 된다.
테이블은 마치 책장에 책을 막 넣은 것처럼 중간에 순서가 섞여 있는 경우가 대부분이다.
인덱스와 실제 테이블을 연결하는 고리는 ROWID라는 존재이다.ROWID는 데이터베이스 내의 주소에 해당하는데 모든 데이터는 자신만의 주소를 가지고 있다.
SQL을 통해서 bno 값이 100번인 데이터를 찾고자 할 때는 SQL은 where bno=100과 같은 조건을 주게 된다.
이를 처리하는 데이터베이스 입장에서는 tbl_board라는 책에서 bno 갑이 100인 데이터를 찾아야만 한다.
만일 책이 얇아서 내용이 많지 않다면 속히 전체를 살펴보는 것이 더 빠를 것이다(이를 데이터베이스 쪽에서는 FULLSCAN이라고 표현한다.)하지만 내용이 많고 색인이 존재한다면 당연히 색인을 찾고 색인에서 주소를 찾아서 접근하는 방식을 이용할 것이다.

실행 계획을 보면 이러한 생각이 데이터베이스 내에서 진행되는 것을 확인할 수 잇다.
안쪽을 먼저 보면 PK_BOARD는 인덱스이므로 먼저 인덱스를 이용해서 100번 데이터가 어디에 있는지 ROWID를 찾아내고 바깥쪽을 보면 BY INDEX ROWID라고 되어 있는 말 그대로 ROWID를 통해서 테이블에 접근하게 된다.
인덱스를 이용하는 정렬
인덱스에서 가장 중요한 개념 중 하나는 정렬이 되어 있다는 점이다.
정렬이 이미 되어있는 상태이므로 데이터를 찾아내서 이들을 sort하는 과정을 생략할 수 있다.
bno의 역순으로 정렬한 결과를 원한다면 이미 정렬된 인덱스를 이용해서 뒤에서부터 찾아 올라가는 방식을 이용할 수 있다.이때 뒤에서부터 찾아 올라간다는 개념이 descending이다.이전에 실행한 bno의 역순으로 데이터를 가져올 때의 실행 계획을 다시 한 번 확인해보자.
실행 계획을 보면 PK_BOARD라는 인덱스를 이용하는데 descending을 하고 있는 것을 볼 수 있다.
먼저 bno가 가장 큰 것부터 찾아올라간다.
인덱스를 역순으로 찾기 때문에 가장 먼저 찾은 bno 값은 가장 큰 값을 가진 데이터가 된다.
이후에는 테이블에 접근해서 데이터를 가져오는데 이런 과정이 반복되면 정렬을 하지 않아도 동일하게 정렬된 결과를 볼 수 있게 된다.
하나의 예를 들면 만일 사용자가 bno의 순서로 정렬해 달라고 요구하는 상황이라면 pk_board 인덱스가 앞에서부터 찾아서 내려가는 구조를 이용하는 것이 효율적이다.SQL Developer를 이용해서 실행해 보면 아래와 같은 실행 계획이 수립되는 것을 볼 수 있다.

실행 계획상으로 pk_board 인덱스를 먼저 접근하고 tbl_board를 이용하는 것을 볼 수 있다.sort가 없기 때문에 0초에 가까운 성능을 보여준다.실무에서도 데이터의 양이 많고 정렬이 필요한 상황이라면 우선적으로 생각하는 것이 인덱스를 작성하는 것이다.데이터의 양이 수천,수만개 정도의 정렬은 그다지 부하가 걸리지 않지만 그 이상의 데이터를 처리해야 하는 상황이라면 정렬을 안할 수 있는 방법에 대해서 고민해야 한다.
인덱스와 오라클 힌트
웹페이지의 목록은 주로 시간의 역순으로 정렬된 결과를 보여준다.
최신 데이터가 가장 중요하기 때문에 시간의 역순으로 정렬해서 최신 게시물들을 보여주게 된다.
이 경우 개발자의 입장에서는 정렬을 안 하는 방식으로 select문을 실행하고 싶어 한다.
오라클은 select문을 전달할 때 힌트라는 것을 사용할 수 있다.
힌트는 말 그대로 데이터베이스에 지금 내가 전달한 select문을 이렇게 실행해 주면 좋겠다 라는 힌트이다.
힌트는 특이하게도 select문을 어떻게 처리하는지에 대한 얘기일 뿐이므로 힌트 구문에서 에러가 나도 전혀 SQL 실행에 지장을 주지 않는다.다라서 힌트를 이용한 select문을 작성한 후에는 실행 계획을 통해서 개발자가 원하는 대로 SQL이 실행되는지를 확인하는 습관을 가져야 한다.
게시물 목록은 반드시 시간의 역순으로 나와야만 하기 때문에 SQL에서는 order by bno desc와 같은 구문을 추가할 수 있다.문제는 order by bno desc와 같은 조건은 데이터베이스 상황에 따라서 테이블의 모든 데이터를 정렬하는 방식으로도 동작할 수 있다는 점이다.
반면에 힌트는 개발자가 데이터베이스에 어떤 방식으로 실행해 줘야 하는지를 명시하기 때문에 조금 강제성이 부여되는 방식이다.
select * from tbl_board order by bno desc;
select /*+index_desc(tbl_board pk_board)*/ *from tbl_board;
위의 두 쿼리는 동일한 결과를 생성하는 쿼리이다.
두 번째 select문은 order by 조건이 없어도 동일한 결과가 나온 것에 주목해야 한다.
select문에서 힌트를 부여했는데 힌트의 내용이 tbl_board 테이블에 pk_board 인덱스를 역순으로 이용해 줄 것 이므로 실행 계획에서 이를 활용하고 있는 것을 확인할 수 있다.
힌트 사용 문법
select문을 작성할 때 힌트는 잘못 작성되어도 실행할 때는 무시되기만 하고 별도의 에러는 발생하지 않는다.
우선 힌트를 사용할 때에는 다음과 같은 문법을 사용한다.
select
/*hint name(param...)*/column name, ....
from
table name
....
힌트 구문은 /*+로 시작하고 */로 마무리 된다.힌트 자체는 SQL로 처리되기 않기때문에 위의 쿼리문처럼 칼럼명이 나오더라도 별도의 , 로 처리되지 않는다.
FULL 힌트
힌트 중에는 해당 select문을 실행할 때 테이블 전체를 스캔할 것으로 명시하는 FULL힌트가 있다.
FULL힌트는 테이블의 모든 데이터를 스캔하기 때문에 데이터가 많을 때는 상당히 느리게 실행된다.
예를 들어 tbl_board 테이블을 FULL 스캔하도록 하고 이상트에서 정렬을 하려면 다음과 같이 작성할 수 있다.
select /*+full(tbl_board)*/ *from tbl_board order by bno desc;

실행 계획을 보면 tbl_board를 FULL로 접근하고 다시 sort가 적용된 것을 볼 수 있다.
INDEX_ASC,INDEX_DESC 힌트
흔히 목록 페이지에서 가장 많이 사용하는 힌트는 인덱스와 관련된 index_asc,index_desc 힌트이다.
asc/desc에서 알 수 있듯이 인덱스를 순서대로 이용할 것인지 역순으로 이용할 것인지를 지정하는 것이다.
index_asc.desc 힌트는 주로 order by를 위해서 사용한다고 생각하면 된다.인덱스 자체가 정렬을 해 둔 상태이므로 이를 통해서 sort 과정을 생략하기 위한 용도이다.
index_asc/desc 힌트는 테이블 이름과 인덱스 이름을 같이 파라미터로 사용한다.

index_asc/desc를 이용하는 경우에는 동일한 조건의 order by 구문을 작성하지 않아도 된다.
예를 들어 위의 SQL에서 아무런 order by 조건이 없어도 bno의 순번을 통해서 접근하기 때문에 order by bno asc 구문은 필요하지 않는다.
ROWNUM과 인라인뷰
ROWNUM은 쉽게 생각해서 SQL이 실행된 결과에 넘버링을 해준다고 생각하면 된다.
모든 select문에는 ROWNUM이라는 변수를 이용해서 해당 데이터가 몇 번째로 나오는지 알아낼 수 있다.
ROWNUM은 실제 데이터가 아니라 테이블에서 데이터를 추출한 후에 처리되는 변수이므로 상황에 따라서 그 값이 매번 달라질 수 있다.
우선 아무 조건을 적용하지 않고 tbl_board 테이블에 접근하고 각 데이터에 ROWNUM을 적용하면 다음과 같이 작성할 수 있다.
SQL에 아무런 조건이 없기 때문에 데이터는 테이블에 섞여 있는 상태 그대로 나오게 된다.(테이블에 FULL 스캔한 것과 동일하다.각자 사용하는 데이터베이스에 저장된 구조가 다르므로 결과들이 동일하지 않으나 섞여있다는 점은 동일하다.)
select ROWNUM rn,bno,title from tbl_board;

ROWNUM은 테이블에는 존재하지 않고 테이블에서 가져온 데이터를 이용해서 번호를 매기는 방식으로 위의 결과는 테이블에서 가장 먼저 가져올 수 있는 데이터들을 꺼내서 번호를 붙여주고 있다.이때 번호는 현재 데이터베이스의 상황에 따라서 저장된 데이터를 로딩하는 것이므로 환경에 따라 다르게 나올 수 있다.
위의 결과를 보면
25번 데이터는 3번째로 꺼내진 데이터라고 해석할 수 있다.만일 테이블에서 데이터를 가져온 후에 정렬을 한다면 25번의 ROWNUM 값은 동일하게 3이 된다.
select /* full(tbl_board)*/rownum rn,bno,title from tbl_board where bno>0 order by bno;

위의 SQL은 FULL 힌트를 이용해서 전체 데이터를 조회하고 다시 정렬한 방식이다.
결과를 보면 25번 데이터는 3번째로 접근되었지만 정렬 과정에서 뒤쪽으로 밀리는 것을 볼 수 있다.
이를 통해서 알 수 있는 사실은 ROWNUM이라는 것은 데이터를 가져올 때 적용되는 것이고
이 후에 정렬되는 과정에서는 ROWNUM이 변경되지 않는다는 것이다.
다른말로 정렬은 나중에 처리된다는 의미이기도 하다.
인덱스를 이용한 접근 시 ROWNUM
ROWNUM의 의미가 테이블에서 데이터를 가져오면서 붙는 번호라는 사실을 기억해 보면 결국 문제는 테이블에 어떤 순서로 접근하는가에 따라서 ROWNUM 값은 바뀔 수 있다는 뜻이 된다.
다시말해 위의 경우는 우선 FULL로 전근해서 25번 데이터를 찾았고 이후에 정렬을 하는데 이미 데이터는 다 가져온 상태이므로 ROWNUM에는 아무런 영향을 주지 않는다.
만일 PK_BOARD 인덱스를 통해서 접근한다면 다음과 같은 과정으로 접근한다.
- PK_BOARD 인덱스를 통해서 테이블에 접근
- 접근한 데이터에 ROWNUM 부여
1의 과정에서 이미 정렬이 되었기 때문에 25번의 접근 순서는 3번째가 아니라 한참 뒤일 것이다.
이 경우 ROWNUM은 전혀 다른 값을 가지게 된다.
select /*+ index_asc(tbl_board pk_board)*/
rownum rn,bno,title,content
from tbl_board;
위의 SQL은 인덱스를 찾는 순서가 다르므로 아래와 같은 방식으로 실행되게 된다.

힌트를 이용해서 tbl_board 테이블을 pk_board의 순번으로 접근하게 되면 ROWNUM의 값이 5번으로 달라진 것이 보인다.

만일 게시물의 역순으로 테이블에 접근하게 된다면 25번의 ROWNUM 값은 접근하는 순서가 뒤쪽이기 때문에 엄청나게 큰 값이 나오게 된다.ROWNUM은 데이터를 접근하는 데이터가 1번이 되는데 이를 이용하면 테이블을 bno의 역순으로 접근해서 bno 값이 가장 큰 데이터가 ROWNUM 값이 1이 되도록 작성할 수 있다.(index_desc)
select /*+ index_desc(tbl_board pk_board)*/
rownum rn,bno,title,content from tbl_board
where bno>0;

위의 SQL은 PK_BOARD 인덱스를 역으로 타면서 테이블에 접근했기 때문에 bno 값이 가장 높은 데이터를 가장 먼저 가져오게 된다.이 방식을 이용하면 각 게시물을 정렬하면서 순번을 매겨줄 수 있는데,1페이지의 경우는 사진처럼 rn이라는 칼럼의 값이 1부터 10에 해당한다고 볼 수 있다.(10개씩 페이징을 한다는 전제)
페이지 번호 1,2의 데이터
한 페이지당 10개의 데이터를 출력한다고 가정하면 ROWNUM 조건을 where 구문에 추가해서 다음과 같이 작성할 수 있다.
select /*+ index_desc(tbl_board pk_board)*/
rownum rn,bno,title,content from tbl_board
where rownum<=10;
where 구문에는 ROWNUM 관련 조건을 줄 수 있는데 위의 SQL 처리 결과는 다음과 같다.

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이 포함되도록 다음과 같이 수정해보면 결과가 나오는 것을 볼 수 있다.
--ROWNUM은 반드시 1이 포함되도록 해야 한다.
select /* _index_desc(tbl_board pk_board)*/
rownum rn,bno,title,content
from
tbl_board
where rownum<=20;
달라진 점은 rownum 조건이 1을 포함하도록 변경한 것뿐이다.위의 SQL 결과는 아래와 같이 역순으로 데이터를 20개 가져오게 된다.

인라인뷰(in-line-view) 처리
10개씩 목록을 출력하는 경우 2페이지의 데이터 20개를 가져오는 데는 성공하였지만
1페이지의 내용이 같이 출력되는 문제가 있으므로 마지막으로 이 문제를 수정해야 한다.
이 문제를 해결하기 위해서는 인라인뷰라는 것을 이용하는데 인라인뷰를 쉽게 설명하자면
select문 안쪽 from에 다시 select문 으로 이해할 수 있다.인라인뷰는 논리적으로 어떤 결과를 구하는 select문이 있고
그 결과를 다시 대상으로 삼아서 select를 하는 것이다.
데이터베이스에서는 테이블이나 인덱스와 같이 뷰(view)라는 개념이 존재한다.
뷰는 일졸의 창문같은 개념으로 복잡한 select 처리를 하나의 뷰로 생성하고,사용자들은 뷰를 통해서 복잡하게 만들어진 결과를 마치 하나의 테이블처럼 쉽게 조회한다는 개념이다.
인라인뷰는 이러한 뷰의 작성을 별도로 작성하지 않고 말그대로 from 구문안에 바로 작성하는 형태이다.

외부에서 select 문은 인라인뷰로 작성된 결과를 마치 하나의 테이블처럼 사용한다.
예를 들어 위의 경우 20개의 데이터를 가져오는 SQL을 하나의 테이블처럼 간주하고 바깥쪽에서 추가적인 처리를 하는 것이다.

만일 위의 결과를 하나의 테이블로 보면 해당 테이블은 rn,bno,title,content 라는 칼럼을 가지는 테이블이 된다.
이 경우 이 테이블에서 원하는 것은 rn 칼럼 값이 10보다 큰 데이터만 가져오게 된다.
인라인뷰를 적용한 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과 같은 조건을 이용해서 구한다.
- 구해놓은 데이터를 하나의 테이블처럼 간주하고 인라인뷰로 처리한다.
- 인라인뷰에서 필요한 데이터만을 남긴다.
'Spring Framework' 카테고리의 다른 글
| 스프링 MVC 프로젝트 - 페이징화면 처리 (0) | 2021.07.22 |
|---|---|
| 스프링 MVC 프로젝트 - MyBatis와 스프링에서 페이징 처리 (0) | 2021.07.19 |
| 스프링 MVC 프로젝트 - 화면처리 (0) | 2021.07.14 |
| 스프링 MVC 프로젝트 - 프레젠테이션(웹)계층의 CRUD 구현 (0) | 2021.07.08 |
| 스프링 MVC 프로젝트 (0) | 2021.07.06 |
