메뉴 건너뛰기

tnt_db

CREATE TABLE TEST01 (A VARCHAR2(10) PRIMARY KEY, B NUMBER);
DECLARE
PL_CNT NUMBER;
BEGIN
  PL_CNT :=0;
  LOOP
  PL_CNT := PL_CNT + 1;
  EXIT WHEN PL_CNT > 1000;
  INSERT INTO TEST01 VALUES
  (LPAD(TO_CHAR(PL_CNT),5,'0'),PL_CNT);
  END LOOP;
END;

누계 COLUMN이 없는 TABLE에 누계 값을 보자 (부등호 JOIN )

조건.
     코드      값
     ----    ----
     0001       1
     0002       2
     0003       3
     0004       4
     0005       5
     0006       6
     0007       7
     0008       8
     0009       9
     0010      10
       .        .
       .        .
       .        .
       .        .

     와 같은 DATA가 TEST01 이라는 TABLE에 존재한다.
     편의상 코드의 COLUMN 명은 A    값의 COLUMN명은 B 라 하자.
     특정 KEY값을 기준으로 (날자,숫자 등등) 값(NUMBER 타입)을 가진
     COLUMN의 COLUMN 값과 함께 그 상위값들의 누계값을 보고자 하는 경우가 있다.
     부등호 JOIN의 좋은 예라고 할 수 있다.
    
문제 .
     다음과 같이 누계도 함께 보고자 하는 것이다.

     코드      값   누계값
     ----    ----   ------
     0001       1        1
     0002       2        3
     0003       3        6
     0004       4       10
     0005       5       15
     0006       6       21
     0007       7       28
     0008       8       36
     0009       9       45
     0010      10       55
       .        .        .
       .        .        .
       .        .        .
       .        .        .

생각 .
     어떻게 해결해야할지 잠시 생각을 해보고 엿보기를 보자.
     방법이 떠오르면 그대로 해보고 비교해보는 것도 좋을 것이다.
     여기서 제시되는 방법이 항상 최적은 아니다.
     단지 한가지 방법에 불과할 뿐이다.
     엿보기1.
            각 RECORD 마다 자기보다 KEY값이 작거나 같은 값을 모두 골라서
            SUM을 해주면된다.
     엿보기2.
            자기TABLE의 값을 참조해야하므로 자기 TABLE과 JOIN을 해야할까?
     엿보기3.
            JOIN은 부등호로도 이루어질 수 있다.

해법 .
     단계1.엿보기 내용을 그대로 실천하면된다.
           먼저 누계없는 값을 읽어오면 다음과 같다.

           SELECT  T01.A,
                   T01.B
           FROM    TEST01 T01
           WHERE   T01.A > ' '
        
           A COLUMN에 INDEX가 있다면 A값의 순서대로 SORT되서 나올것이고
           그렇지 않다고 해도 문제될것은 없으므로 WHERE절은 없어도 좋다.
           PLAN 상의 차이는 있을 것이다.
           즉,범위를 주어주고 해당 범위의 DATA만을 다룬다면 위의 조건이
           있어야만 INDEX SCAN을 하게된다.
           뒤에서 자세히 살펴보기로 하고 우선 원하는 결과를 얻는방법을
           생각해 보자.

           SELECT  T02.A,
                   T02.B
           FROM    TEST01 T02
           WHERE   T02.A > ' '

           같은값이 다른 ALIAS를 가지고 읽혀져왔다.
           둘중에 하나를 다른쪽보다 작거나같다라는 조건으로 JOIN을
           걸어서 SUM 해주면 답이다.

     단계2.이렇게....

           SELECT  T01.A,
                   T01.B,
                   SUM(T02.B)
           FROM    TEST01  T01,
                   TEST01  T02
           WHERE   T02.A  <= T01.A
           GROUP BY
                   T01.A,T01.B

PLAN
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (GROUP BY)
   2    1     NESTED LOOPS
   3    2       TABLE ACCESS (FULL) OF 'TEST01'
   4    2       TABLE ACCESS (BY ROWID) OF 'TEST01'
   5    4         INDEX (RANGE SCAN) OF 'SYS_C00384' (UNIQUE)




Statistics
----------------------------------------------------------
          0  recursive calls
          2  db block gets
    1003009  consistent gets
          0  physical reads
          0  redo size
      25782  bytes sent via SQL*Net to client
       1215  bytes received via SQL*Net from client
         69  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
       1000  rows processed

뒷풀이 .
     문제가 있다면 RECORD수가 늘어날수록 처리시간이 지수곡선을 그리며
     증가한다는 것이다.
     잠간만 생각해봐도 쉽게 원인을 알 수 있다.
     0001 은 자기 보다 작거나같은값이 하나
     0002 는 2개 0003은 3개 0004는 4개의 RECORD와 JOIN이 이루어지면
     0010 까지만 55개의 RECORD가 필요하게된다.
     이런식으로 증가해서 10000건만되어도 50005000(5천5백만)건의 RECORD가
     JOIN으로 인해 생성되어 처리된다.
     결국 RESPONSE TIME을 고려한다면 조회의 경우
     백단위내 에서만 효과가 있다는 얘기다.
     벙위를 주어주고 해당범위 안의 값을 이용해 DATA가 처리된다면
     효율적일 수 있다.
     예를들어 값이 100 에서 199 까지 인것만을 처리하고자 한다면
     다음과 같이 할 수 있을 것이다.

     SELECT  T01.A,
             T01.B,
             SUM(T02.B)
     FROM    TEST01  T01,
             TEST01  T02
     WHERE   T01.A BETWEEN  '00100' AND '00199'
     AND     T02.A BETWEEN  '00100' AND '00199'
     AND     T02.A <= T01.A
     GROUP BY
             T01.A,T01.B

PLAN
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (GROUP BY)
   2    1     NESTED LOOPS
   3    2       TABLE ACCESS (BY ROWID) OF 'TEST01'
   4    3         INDEX (RANGE SCAN) OF 'SYS_C00384' (UNIQUE)
   5    2       TABLE ACCESS (BY ROWID) OF 'TEST01'
   6    5         INDEX (RANGE SCAN) OF 'SYS_C00384' (UNIQUE)




Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      10584  consistent gets
          0  physical reads
          0  redo size
       2708  bytes sent via SQL*Net to client
        613  bytes received via SQL*Net from client
          9  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        100  rows processed

     1000건과 100건의 실행시간을 비교해 보면 다음과 같다.

     1000 행이 선택되었습니다
     구문 분석        0.00 (경과됨)      0.00 (CPU)
     실행/인출        16.80 (경과됨)      0.00 (CPU)
     합계             16.80               0.00
    
     100 행이 선택되었습니다
     구문 분석        0.00 (경과됨)      0.00 (CPU)
     실행/인출         0.38 (경과됨)      0.00 (CPU)
     합계              0.38               0.00

     결과 DATA의 ROW수는 10배이지만 실행시간은 단순한 10배가 아니라는
     것을 눈으로 확인할 수 있다.

     참고로 다른방법이지만 같은문제를 해결하는 방법을 소개하면
     이해를 도울수 있을지 모르겠다.
     동일한 문제를 다음주제에서는 다른방법이지만 같은 흐름으로
     해결해보도록하자.
     그러기 위해서 먼저 이해하고 넘어야 할 부분을 다루도록하겠다.
위로