부분범위 처리

pivate void execute(Connection con) throws Exception {
    Statement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT NAME FROM TBL");

    for(int i = 0; i < 100; ++i) {
        if(rs.next()) System.out.println(rs.getString(1));
    }

    rs.close();
    stmt.close();
}

정렬 조건이 있을 때 부분범위 처리

Array Size 조정을 통한 Fetch Call 최소화

부분범위 처리 구현

/** 
 * 부분범위 처리 X
*/
public class AllRange {
    public static void execute(Connection con) throws Exception {
        int arraySize = 10;
        String SQLStmt = "SELECT ID, NAME FROM TBL";
        Statement stmt = con.createStatement();
        stmt.setFetchSize(arraySize);
        ResultSet rs = stmt.executeQuery(SQLStmt);
        while(rs.next()) {
            System.out.println(rs.getLong(1) + " " + rs.getString(2));
        }

        rs.close();
        stmt.close();
    }

    public static void main(String[] args) throws Exception {
        Connection con = getConnection();
        execute(con);
        releaseConnection(con);
    }
}

/** 
 * 부분범위 처리 O
*/
public class PartitialRange {
    public static int fetch(Resultset rs, int arraySize) throws Exception {
        int i = 0;
        whlie(rs.next()) {
            System.out.println(rs.getLong(1) + " " + rs.getString(2));
            if(++i >= arraySize) return i; // 출력 레코드 수가 Array Size에 도달하면 멈춤
        }
        return i;
    }

    public static void execute(Connection con) throws Exception {
        int arraySize = 10;
        String SQLStmt = "SELECT ID, NAME FROM TBL";
        Statement stmt = con.createStatement();
        stmt.setFetchSize(arraySize);
        ResultSet rs = stmt.executeQuery(SQLStmt);
        
        // 사용자 요청이 있을 때 다시 데이터를 Fetch
        while(true) {
            int r = fetch(rs, arraySize);
            if(r < arraySize) break;
            System.out.println("Enter to Continue... (Q)uit?");
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            String input = in.readLine();
            if(input.equals("Q")) break;

        }

        rs.close();
        stmt.close();
    }

    public static void main(String[] args) throws Exception {
        Connection con = getConnection();
        execute(con);
        releaseConnection(con);
    }
}

OLTP 환경에서 부분범위 처리에 의한 성능개선 원리

select 게시글ID, 제목, 작성자, 등록일시
from   게시판
where  게시판구분코드 = 'A'
order by 등록일시 desc

--------------------------------------------------------------------------------------------
| Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |            | 40000 | 3515K |  2041   (1)|
|   1 |  SORT ORDER BY                |            | 40000 | 3515K |  2041   (1)|
|   2 |   TABLE ACCESS BY INDEX ROWID | 게시판     | 40000 | 3515K |  1210   (1)|
|* 3 |    INDEX RANGE SCAN           | 게시판_X01 | 40000 |       |    96   (2)|
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("게시판구분코드"='A')
-- (게시판구분코드 + 등록일시) 인덱스로 구성된 경우 Sort Order By 연산 생략
--------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name       | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |            | 40000 | 3515K |  1372   (1)|
|   1 |  TABLE ACCESS BY INDEX ROWID     | 게시판     | 40000 | 3515K |  1372   (1)|
|* 2 |   INDEX RANGE SCAN DESCENDING    | 게시판_X02 | 40000 |       |   258   (1)|
--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("게시판구분코드"='A')

멈출 수 있어야 의미있는 부분범위 처리

배치 I/O

데이터 정렬 이슈

-- 인덱스를 통한 Sort 연산 생략
SQL> create index emp_x01 on emp(deptno, job, empno);

SQL> set autotrace traceonly exp;

SQL> select * from emp e where deptno = 20 order by job, empno;

---------------------------------------------------------------------------------------
| Id  | Operation                   | Name    | Rows  | Bytes | Cost |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |         |     5 |   190 |    2 |
|   1 |  TABLE ACCESS BY INDEX ROWID| EMP     |     5 |   190 |    2 |
|   2 |   INDEX RANGE SCAN          | EMP_X01 |     5 |       |    1 |
---------------------------------------------------------------------------------------
SQL> select /*+ batch_table_access_by_rowid(e) */ *
  2  from   emp e
  3  where  deptno = 20
  4  order by job, empno;

---------------------------------------------------------------------------------------
| Id  | Operation                        | Name    | Rows  | Bytes | Cost |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |         |     5 |   190 |    2 |
|   1 |  SORT ORDER BY                   |         |     5 |   190 |    2 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMP     |     5 |   190 |    2 |
|   3 |    INDEX RANGE SCAN              | EMP_X01 |     5 |       |    1 |
---------------------------------------------------------------------------------------
-- 인덱스로 소트 생략이 불가능한 경우
SQL> select * from emp e where deptno = 20 order by empno;

---------------------------------------------------------------------------------------
| Id  | Operation                        | Name    | Rows  | Bytes | Cost |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |         |     5 |   190 |    3 |
|   1 |  SORT ORDER BY                   |         |     5 |   190 |    3 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| EMP     |     5 |   190 |    2 |
|   3 |    INDEX RANGE SCAN              | EMP_X01 |     5 |       |    1 |
---------------------------------------------------------------------------------------

-- ORDER BY가 없는 경우
SQL> select * from emp e where deptno = 20;

---------------------------------------------------------------------------------------
| Id  | Operation                        | Name    | Rows  | Bytes | Cost |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |         |     5 |   190 |    2 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| EMP     |     5 |   190 |    2 |
|   2 |   INDEX RANGE SCAN              | EMP_X01 |     5 |       |    1 |
---------------------------------------------------------------------------------------
-- 1. 상태변경이력_PK : 장비번호 + 변경일시
SELECT /*+ INDEX(H 상태변경이력_PK) */ 장비번호, 변경일시, 상태코드
FROM   상태변경이력 H
WHERE  장비번호 = :eqp_no
AND    ROWNUM <= 10; -- 변경일시 순으로 상위 10개 레코드 출력


-- 2. 인덱스 튜닝 예시 (스칼라 서브쿼리 활용)
SELECT 장비번호, 장비명, 상태코드
     , (SELECT /*+ INDEX_DESC(H 상태변경이력_PK) */ 변경일시
        FROM   상태변경이력 H
        WHERE  장비번호 = P.장비번호
        AND    ROWNUM <= 1) 최종변경일시 -- 변경일시 역순으로 상위 1개 레코드 조회
FROM   장비 P
WHERE  장비구분코드 = 'A001';
SELECT /*+ INDEX(H 상태변경이력_PK) */ 장비번호, 변경일시, 상태코드
FROM   상태변경이력 H
WHERE  장비번호 = :eqp_no ;