메뉴 건너뛰기

tnt_db

누계 COLUMN이 없는 TABLE에 누계 값을 보자 (IN-LINE VIEW)
        
조건.
     조건은
     누계 COLUMN이 없는 TABLE에 누계 값을 보자 (부등호 JOIN )
     조건과 동일하다.
문제.
     누계 COLUMN이 없는 TABLE에 누계 값을 보자 (부등호 JOIN )
     와 동일한 결과가 나오도록 하되 다음과 같은방법을 이용한다.

      코드      값    코드      값      코드      값    코드    값  
     ----    ----     ----    ----     ----    ----     ----  ----
     0001       1
     0002       2  <- 0001       1
     0003       3  <- 0002       2  <- 0001       1
     0004       4  <- 0003       3  <- 0002       2  <- 0001     1
     0005       5  <- 0004       4  <- 0003       3  <- 0002     2
     0006       6  <- 0005       5  <- 0004       4  <- 0003     3
     0007       7  <- 0006       6  <- 0005       5  <- 0004     4
     0008       8  <- 0007       7  <- 0006       6  <- 0005     5
     0009       9  <- 0008       8  <- 0007       7  <- 0006     6
     0010      10  <- 0009       9  <- 0008       8  <- 0007     7
       .        .  <- 0010      10  <- 0009       9  <- 0008     8
       .        .  <-   .        .  <- 0010      10  <- 0009     9
       .        .  <-   .        .  <-   .        .  <- 0010    10
       .        .  <-   .        .  <-   .        .  <-    .      .
생각.
    이미 앞서서 풀어봤던 문제를 다른방법을 이용해 해결하고자
    하는것이다.
    단지 같은방법의 전체 LOGIC을 직접 풀어헤쳐서 비교를 해보고자
    하는 것이 목적이다.
    생각하기에 따라서는 간단할 수 있지만 주의깊게 생각해 보기를
    바란다.
    엿보기1.각 RECORD가
          바로 이전 Record의 값을 참조하고자 할때.
          에서 사용했던방법을 확대 적용하여 사용하면된다.
          즉 0010 이란 코드를 가진 DATA는 전번RECORD의 0009에 같은
          GROUPING 단위를 부여하고 그전 RECORD 인 0008 에도 같은
          GROUPING 단위를 부여하며  그런식으로 0001 까지에 같은
          GROUPING 단위를 부여하여 GROUP BY 시켜주면 된다.  
          바로전 주제는 단지 전번 RECORD의 값을 참조했지만 이번에는
          자기보다 작은 값을 가지는 모든 RECORD를 참조하는 것이다.
    엿보기2.
          그러기 위해서 각 RECORD 하나에 대해 자기보다 작은값을
          가지는 RECORD 수 만큼의 JOIN이 걸린다.
    엿보기3.
          그리고 JOIN에 의해 생성된 집합의 각 RECORD가
          어떤GROUPING  단위를 가질것인가를 결정해 주면 된다.
    엿보기4.
          결정된 GROUPING 단위로 GROUP을 지으면 원래 TABLE에 있는
          각 RECORD가 GROUPING 단위가 되어 누계가 나오게 되고
          그것을 원래값과 JOIN시켜주면 원하는 값이 나올것이다.
          그림으로보면 아래와 같다.
          모든값은 자신보다 작은값 전체에 대하여
          한번씩 자신과 같은 GROUPING 단위를
          부여한다.

   코드  값     코드  값     코드  값     코드  값    group 단위
   ---- ---     ---- ---     ---- ---     ---- ---
   0001   1                                         <--   1
   0002   2  <- 0001   1                            <--   2
   0003   3  <- 0002   2  <- 0001   1               <--   3
   0004   4  <- 0003   3  <- 0002   2  <- 0001   1  <--   4
   0005   5  <- 0004   4  <- 0003   3  <- 0002   2  <--   5
   0006   6  <- 0005   5  <- 0004   4  <- 0003   3  <--   6
   0007   7  <- 0006   6  <- 0005   5  <- 0004   4  <--   7
   0008   8  <- 0007   7  <- 0006   6  <- 0005   5  <--   8
   0009   9  <- 0008   8  <- 0007   7  <- 0006   6  <--   9
   0010  10  <- 0009   9  <- 0008   8  <- 0007   7  <--   10
     .    .  <- 0010  10  <- 0009   9  <- 0008   8  <--   11
     .    .  <-   .    .  <- 0010  10  <- 0009   9  <--   12
     .    .  <-   .    .  <-   .    .  <- 0010  10  <--   13
     .    .  <-   .    .  <-   .    .  <-    .   .  <--   14
   --------     --------     --------     --------
    현재값       직전값      2번째전값    3번째전값     ..........

해법.
    단계1.엿보기를 이해했다면 혼자서도 시도해 볼만하다.
          그렇지 않다면 한단계씩 밟아보자.

          SELECT ROWNUM   RCNT,
                             B
          FROM   TEST01
          위와같은 QUERY를 만들어서 IN-LINE VIEW로 사용하고
          명칭을 GR1_1 이라 하자.

          SELECT ROWNUM   JCNT
          FROM   TEST01,
          위와같은 QUERY를 만들어서 IN-LINE VIEW로 사용하고 명칭을
          GR1_2 이라 하자.
    단계2.두개의 TABLE을 JOIN하면 RECORD수가 그 제곱만큼으로
          늘어나는 DATASET이 생기며 (CARTESIAN PRODUCT),
          RCNT는 GROUPING 단위가  부여될 값을 가지게 되며
          JCNT는 1을 기본 RECORD로 2는 참조하고자하는 전값,
          3은 전전값,4는 그전값 식으로 순위를 가지게된다.

          SELECT DECODE(JCNT,1,RCNT,
                               RCNT -1 + JCNT) R_CNT,<-그림으로설명
                 B,
                 JCNT
          FROM  (SELECT ROWNUM   RCNT,
                        B
                 FROM   TEST01) GR1_1,
                (SELECT ROWNUM   JCNT
                 FROM   TEST01) GR1_2


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

     쉽게 말해서 RCNT 에 대하여 JCNT 를 첨자로하는 2차원 배열이
     생기게된다.
     하나의 DATASET으로 불려온 RECORD들을 JCNT에따라 나눠놓았다.
     JCNT 가 1 이면 RCNT 값을,
     그보다크면 RCNT - 1 + JCNT의 값을
     SUM을위한 GROUPING 단위로 가지면 된다.
     얼핏 이해가 안되면 그림을 다시 그려보자

     JCNT가 2인 것을 참고로 하겠다.

      코드     값   RCNT -1 + JCNT
     ----    ----   --------------
     0001       1                2
     0002       2                3
     0003       3                4
     0004       4                5
     0005       5                6
     0006       6                7
     0007       7                8
     0008       8                9
     0009       9               10
     0010      10               11
       .        .                .
       .        .                .
       .        .                .
       .        .                .
     JCNT가 2인 경우
     RCNT -1 + JCNT 의 값이 GROUPING단위로 사용되면
     기준이되는 JCN = 1 인 RECORD 중에 RCNT가 같은 RECORD와 연결된다.

     다시 설명하자면
     추출된 모든 RECORD 는 RCNT와 JCNT를 갖는다.
     JCNT를 기준으로하든 RCNT를 기준으로 하든 똑같은 결과가 나온다.
     JCNT를 기준으로 생각해보면 JCNT 가 1이면
     RCNT 자신의 값이 GROUP 단위가 되고,
     JCNT 가 2이면 RCNT -1 ,3이면 RCNT -2,4이면 RCNT-3이 동일
     GROUPING 단위가 된다.
     엿보기의 그림으로 이해를 하면 쉬울것이다.

    단계3.그런데 RCNT가 1 이면 그전값을 참조할 필요가 없고
          2면 바로 전값만,3이면 그 전전값 까지만을 참조하면되지
          전체를 다참조할 필요는 없다.
          그것을 해결하기위해 한가지 IN-LINE VIEW를 더하여
          다음과 같이 사용한다.

          SELECT DECODE(JCNT,1,RCNT,RCNT -1 + JCNT) R_CNT,
                 B,
                 JCNT
          FROM  (SELECT ROWNUM   RCNT,
                        B
                 FROM   TEST01)    GR1_1,
                (SELECT ROWNUM   JCNT
                 FROM   TEST01)    GR1_2,
                (SELECT COUNT(*) MAX_CNT
                 FROM   TEST01)    GR1_3
          WHERE  DECODE(JCNT,1,RCNT,RCNT - 1 + JCNT ) <=MAX_CNT
    단계4.그리고 GROUP BY 한다.

          SELECT R_CNT,SUM(B) C
          FROM  (SELECT DECODE(JCNT,1,RCNT,RCNT -1 + JCNT) R_CNT,
                         B,
                         JCNT
                  FROM  (SELECT ROWNUM   RCNT,
                                B
                         FROM   TEST01)    GR1_1,
                        (SELECT ROWNUM   JCNT
                        FROM   TEST01)    GR1_2,
                        (SELECT COUNT(*) MAX_CNT
                         FROM   TEST01)    GR1_3
                  WHERE  DECODE(JCNT,1,RCNT,
                                       RCNT - 1 + JCNT ) <=MAX_CNT
                 )
          GROUP BY R_CNT
          이렇게 하면 각 KEY 값별로 누계를 구하는데 성공했다.
    단계4.이제 원TABLE을 한번 더 읽어서 JOIN을 해 주면된다.

          SELECT TEMP01.A,
                 TEMP01.B,
                 TEMP02.C
          FROM  (SELECT ROWNUM R_CNT,A,B
                 FROM   TEST01) TEMP01,
                (SELECT R_CNT,SUM(B) C
                 FROM  (SELECT DECODE(JCNT,1,RCNT,
                                             RCNT-1+JCNT) R_CNT,
                               B,
                               JCNT
                        FROM  (SELECT ROWNUM   RCNT,
                                      B
                               FROM   TEST01)    GR1_1,
                              (SELECT ROWNUM   JCNT
                               FROM   TEST01)    GR1_2,
                              (SELECT COUNT(*) MAX_CNT
                               FROM   TEST01)    GR1_3
                        WHERE  DECODE(JCNT,1,RCNT,
                                             RCNT-1+JCNT) <=MAX_CNT
                       )
                 GROUP BY R_CNT
                ) TEMP02
          WHERE TEMP01.R_CNT = TEMP02.R_CNT


PALN.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   MERGE JOIN
   2    1     SORT (JOIN)
   3    2       VIEW
   4    3         SORT (GROUP BY)
   5    4           NESTED LOOPS
   6    5             NESTED LOOPS
   7    6               VIEW
   8    7                 SORT (AGGREGATE)
   9    8                   TABLE ACCESS (FULL) OF 'TEST01'
  10    6               VIEW
  11   10                 COUNT
  12   11                   TABLE ACCESS (FULL) OF 'TEST01'
  13    5             VIEW
  14   13               COUNT
  15   14                 TABLE ACCESS (FULL) OF 'TEST01'
  16    1     SORT (JOIN)
  17   16       VIEW
  18   17         COUNT
  19   18           TABLE ACCESS (FULL) OF 'TEST01'

        방법론을 제시하고자 하는 의도였으므로 PLAN에 큰 의미를 둘
        필요는 없다.
        다만 부등호 조인이 어떤방법으로 일어난다는것을 이해했다면
        충분하다.
뒷풀이.
      제대로 설명이 되었는지 모르겠다.
      말이 요점을 정확히 설명하지 못 했다면 각 단계별로 DATASET이
      어떻게 생성되어 가는지 직접 실행시켜보기 바란다.
      약 10건 정도를 가지고 TEST 하는게 좋을듯하다.
      누계 COLUMN이 없는 TABLE에 누계 값을 보자 (부등호 JOIN )와
      실행된 결과 및 RESPONSE TIME도 함께 비교해보라.
      RESPONSE TIME도 실행당시의 환경에만 영향을 받지 않는다면
      거의 차이가 없을 것이다.
      1000개 정도의 RECORD가 들어있는 TABLE로 실행한 결과와 RESPONSE
      TIME을 비교해보면 재미있을 것이다.

      반드시 자신의 TABLE과 JOIN을 걸지 않더라도 방법은 있다.
      다음을 생각해보자
      T100에 다음과 같은 DATA가 있다.

      C1        C2
      -- --------
      A        10
      B        20
      C        30
      D        40

      A에서 D까지 누계를 내는 경우를 가정할 때 다음과 같은 생각을
      해볼 수 있을 것이다.
      A의 C2값은 A,B,C,D 를 읽어 올 때 같이 더해져야 누계가 계산되며
      B의 C2값은 B,C,D …C의 C2값은 C,D 와 같이 자신보다 크거나같은
      RECORD에 함께 더해져야 한다.
      RECORD가 더욱 많은 경우라도 똑같이 적용 될 것이다.

      A는 1,2,3,4에 각각 1개씩 배정하고,
      B는 2,3,4에 1개씩 배정하고
      C는 3,4에 1개씩 배정하고
      D는 4에 1개 배정해 보자.

      방법은 T100 에서 1건의 RECORD를 읽어오면 자신의 TABLE보다
      RECORD가 많은 아무 TABLE의 ROWNUM들과 JOIN을 걸어주자.
      단 자신의 ROWNUM보다 크거나 같은 ROWNUM과 JOIN이 되도록 한다.

      A는 1,2,3,4,5……와  10의 값으로 JOIN이 걸릴 것이고
      B는 2,3,4,5…...와 20의 값으로  
      C는 3,4,5…….와  30의 값으로
      D는 4,5…..와  40의 값으로
      JOIN이 걸릴 것이다.

      USER_TABLES 에 4개 이상의  RECORD가 존재한다면
      다음과 같은 QUERY를 구성해 보자.
      
      select C1,B.b_RCNT,C2
            from  (select rownum a_rcnt,C1,C2 from   T100) a,
                  (select rownum b_rcnt from user_tables ) b
            where b.b_rcnt >= a.a_rcnt
      ORDER BY C1,B.b_RCNT
      

      실행하면 아래와 같은 결과가 나올 것이다.

      C1    B_RCNT        C2
      -- --------- ---------
      A          1        10
      A          2        10
      A          3        10
      A          4        10
      A          5        10
      ~
      B          2        20
      B          3        20
      B          4        20
      B          5        20
      ~
      C          3        30
      C          4        30
      C          5        30
      ~
      D          4        40
      D          5        40

      ~ 부호 부분은 USER_TABLES에 5개보다 많은 RECORD가
        있음으로 인하여 발생하는 쓸모없는 DATA의 생략 부분이다.
        이부분은 나중에 고려해야 할 부분이다.  
      
      이제 다음 과정에서 위의 DATA를 B_RCNT 로 GROUP BY 해보자.
      결과가 어떻게 나오는가?.
      이때 함께 고려할 것은 누계가 아닌 실제 C1과 C2는
      B_RCNT로 GROUP BY 할 경우 MAX로 읽어야 한다.
      위의 DATA를 잘 들여다 보면 당연하다는 것을 알 수 있다.

      문장은 다음과 같이 구성될 것이고
      select max(C1) C1,max(a.C2) C2,sum(a.C2) sum_C2
             from  (select rownum a_rcnt,C1,C2 from   T100) a,
                   (select rownum b_rcnt from user_tables ) b
             where b.b_rcnt >= a.a_rcnt
             group by b.b_rcnt

      결과는 아래와 같을 것이다.
      C1        C2    SUM_C2
      -- --------- ---------
      A         10        10
      B         20        30
      C         30        60
      D         40       100
      D         40       100
      ~

      한가지 문제는 나중에 고려하고자 미루어 놓았던 문제이다.
      우리가 DATA를 복제하기 위해 사용했던 USER_TABLES의
      RECORD가 T100보다 많은 부분때문에 계속 마지막 DATA가
      중복되고 있는 것이다.
      우리가 사전에 T100 의 건수를 알고 있었다면 그만큼만을
      복제용으로 쓰면 되지만(그래서 자기자신의 TABLE과의
      JOIN 즉 SELF JOIN이 더 효율적이다.)
      그렇게 되기 위해서는 해당 TABLE을 한번 더 읽어야 하는
      문제가 있다.
      결국 자신의 TABLE을 한번만 읽어야 한다는 제약때문에
      GROUP BY를 한 번 더 걸어 줄 수 밖에 없다.
      최종문장은 아래와 같이 된다.
      
      SELECT C1,C2,SUM_C2
       FROM (select max(C1) C1,max(a.C2) C2,sum(a.C2) sum_C2
             from  (select rownum a_rcnt,C1,C2 from   T100) a,
                   (select rownum b_rcnt from user_tables ) b
             where b.b_rcnt >= a.a_rcnt
             group by b.b_rcnt)
       GROUP BY
             C1,C2,SUM_C2;
위로