간만에 대형 월척을 낚았다.

현재 페이징 쿼리를 만들어주는 메서드를 보면

       buffer.append("select * from (select rownum as row_num, ")
              .append(getRemoveAlias(tblField))
              .append(" from (select ")
              .append(tblField)
              .append(" from ")
              .append(tblName);
        if(wh!=null && wh.length()>0) {
          buffer.append(" where ")
                .append(wh);
        }       
        buffer.append(" order by ")
              .append(ob);
        if(isDesc) buffer.append(" desc)) ");
        else buffer.append(" asc)) ");
        buffer.append(" where row_num <= ")
              .append((currentPage)*pageSize)
              .append(" and row_num > ")
              .append((currentPage-1)*pageSize);


와 같이 가져온다.

간단히 말해 안쪽에 있는 쿼리에서 데이터를 전부 가져와서는 맨 바깥에서 row_num 으로 페이지를 자른다.
이렇게 되면 문제가 테이블에서 건수가 100만건이라 가정하면 100만건에 대해 전부 select 해서 rownum 을 전부 매겨서 맨 마지막에 페이지를 자르니, 부하가 많아져서 쿼리 속도가 엄청 늦다.

이렇게 하면 안되고, 안쪽에서 미리 페이지 수에 맞는 만큼( 1페이지면 10개, 2페이지면 20개 등 ) 가져오고 마지막에 row_num 으로 잘라준다.

       buffer.append("select * from (select rownum as row_num, ")
              .append(getRemoveAlias(tblField))
              .append(" from (select ")
              .append(tblField)
              .append(" from ")
              .append(tblName);
        buffer.append(" where rownum <= ")
              .append((currentPage)*pageSize);
        if(wh!=null && wh.length()>0) {
          buffer.append(" and ")
                .append(wh);
        }       
        buffer.append(" order by ")
              .append(ob);
        if(isDesc) buffer.append(" desc)) ");
        else buffer.append(" asc)) ");
        buffer.append(" where row_num > ")
              .append((currentPage-1)*pageSize);


이렇게 수정해준다. 라고 생각하면 큰 오산이다.
왜냐면, rownum 은 select 를 해오고, sort 를 하기전에 붙여진다. 즉 위처럼 하면 order by 하기전에 임의로 rownum 이 붙여지고는 order by 된다.

따라서

       buffer.append("select * from (select rownum as row_num, ")
              .append(getRemoveAlias(tblField))
              .append(" from (select ")
              .append(tblField)
              .append(" from ")
              .append(tblName);       
        if(wh!=null && wh.length()>0) {
          buffer.append(" where ")
                .append(wh);
        }       
        buffer.append(" order by ")
              .append(ob);
        if(isDesc) buffer.append(" desc) ");
        else buffer.append(" asc) ");
        buffer.append(" where rownum <= ")
              .append((currentPage)*pageSize)
              .append(" ) ");
       
        buffer.append(" where row_num > ")
              .append((currentPage-1)*pageSize);


이렇게 수정해준다.

"아니 이렇게 하면 전체를 order by 할 때 비용이 많이 들지 않냐"고 궁금해하면 훌륭하다!
order by 되는 곳에 index를 적절히 걸어주면 되고, 맨 안쪽에서 select 를 할 때 그 인덱스를 타기 때문에 성능은 그리 떨어지지 않는다.

참고할만한 페이지는
http://www.oracle.com/technology/global/kr/oramag/oracle/06-sep/o56asktom.html
에 있다.


Posted by 알 수 없는 사용자 :
String sWhere = "AND F1 = '1' AND F2 = '2'";
이런저런 조건에 의해 만들어진 where 변수가 위와 같다면
"WHERE AND" 라는 이상한 구문이 되므로 첫번째 AND를 replace해버리곤 합니다.
그런데 이게 컨디션 따라 "and"로도 썼다가 "AND"로도 썼다가 하다보면 나중에 찾기도 귀찮고
"대문자로만 쓰자~" 고 해봤자 맨정신에 코딩하는사람도 얼마없죠

해서 자바 정규식 옵션을 이용해
sWhere.replaceFirst("(?i)and","");
처럼 처리해주시면 대문자건 소문자건 날려버릴수 있습니다.
다른 옵션도 많겠지만 당장 실용적인거만 적어놓고 또 일하러갑니다.
Posted by 윤재현 :

오라클 파티션 테이블

2009. 7. 1. 11:12 from DB/ORACLE
http://www.oracleclub.com/lecture/1908
후배가 SM하다가 언놈이 로그테이블 날려먹었다고 SBSB 한후에 아는거 있냐고 해서
파티션 테이블이라는걸 처음 봤네요

일단 생성쿼리는 다른 테이블과 동일하고 마지막줄에
       PARTITION BY RANGE (sale_year, sale_month, sale_day)
      
(PARTITION sales_q1 VALUES LESS THAN (2005, 01, 01) TABLESPACE ASSM_TBS1,
       
PARTITION sales_q2 VALUES LESS THAN (2005, 07, 01) TABLESPACE ASSM_TBS2,
       
PARTITION sales_q3 VALUES LESS THAN (2006, 01, 01) TABLESPACE ASSM_TBS3,
       
PARTITION sales_q4 VALUES LESS THAN (2006, 07, 01) TABLESPACE ASSM_TBS4 );
와 같이 추가적인 정의를 적어줍니다.
range에 포함된 필드값에 따라 저장될 파티션이 정해지죠
위의경우 20050101 보다 작은값은 sales_q1에, 20050701보다 작은값은 sales_q2에 저장되는 식입니다.

select는
SELECT sales_no FROM sales PARTITION (sales_q1); 하면 20050101 이하의 값들이 저장된 데이터만 조회하겠죠

처음에 로그테이블을 날렸다고 했듯 자료가 무진장 쌓이고 기간별로만 사용하는 데이터를 저장할때는 사용해도 좋을듯 싶습니다.

결론은 테이블의 저장데이터범위에 따른 파티션을 설정하고 입력은 하던대로, 조회는 파티션별로.

우리회사에서 쓸일은 없겠지만 다른회사 개발자가 덜컥 얘기 꺼냈을때 포커페이스를 유지하시려면 알아두세요
Posted by 윤재현 :