메뉴 건너뛰기

tnt_db

Oracle 빠진이빨찾기 II

운영자 2002.09.18 13:29 조회 수 : 3067 추천:12

조건.
    TEST37 에 다음과 같은 DATA가 있다.

    DATA      
    ----------
    0000      
    0003      
    0006      
    0010      
    0013    

문제.
    중간에 빠진값만을 읽어오는 문제다.
    결과는 다음과 같이 나오면 된다.

    DATA
    -----
     0001
     0002
     0004
     0005
     0007
     0008
     0009
     0011
     0012

    이미 앞에서 다뤄본 문제이다.
    똑같은 유형의 문제를 다시 다루는 이유는 해법을 약간 달리
    해보자는 것이다.
    앞 주제에서 이 문제를 풀때 우리는 MINUS 라는 집합 연산자를
    이용했다.
    이번에는 MINUS 집합연산자를 이용하지 않고,SELF JOIN을 이용한다.
    앞주제에서는 DATA의 최대 ROW수보다 크거나 같은 ROW수를
    가지고 있는 TABLE이 반드시 존재를 하고 있어야 했다.
    만약 TEST37의 DATA FIELD 의 최대값이 매우커서 그 최대값보다
    많은 ROW수를 가진 TABLE이 없을때는 지난번 해법으로는
    해결 불가능하다.
    ROW수가 충분히 많아서 조건을 만족시켜줄 수 있는 그런 TABLE을
    새로 만들어 주지 않는한.
    이번 문제를 통해서는 그 위험을 줄여주자는 것이다.
    힌트를 준다면 최대값보다 크거나 같은 ROW수를 가진 TABLE을
    찾을 필요가 없이,
    중간에 빠진 이빨의 차이가 가장 큰 수 보다 많거나 같은 ROW만
    가진다면 문제를 해결할 수 있는 QUERY 를 요구하고 있는 것이다.
    만약 DATA가 1,3,8,10 과 같이 들어 있다면 빠진 이빨 차이가
    가장 큰 값은 8과 3의 차이인 5 이다.
    위험을 완전히 없애지는 못했지만 많이 줄일 수는 있다.
    만약 최대값이 매우큰 DATA 이면서 빠진 이빨의 차이가 매우커서
    그 차이보다 많은 ROW수를 가진 TABLE이  없다면 이번 방법으로도
    해결할 수 없다.
    그때는 또 다른 방법을 찾아야 할 것이다.

생각.
    이 문제에서 요구하는 해법은 다음과 같다.
    먼저 DATA를 바로전 RECORD 값과 비교해서 차이가 1보다 큰 DATA만
    찾아내고,
    그 (차이값-1) 만큼을 LOOP 을 돌면서 전 RECORD의 값을 기준으로
    1씩 증가시켜 값을 만들어 낸다.
    문제에 주어진 DATA를 이용해 보면 다음과 같다.

    DATA       바로전값    
    ---------- ----------
             0          
             3          0
             6          3
            10          6
            13         10

    DATA 값이 3인 경우를 보면 3과 0의 차이는 3 이다.
    이경우 차이가 1보다 크므로 차이나는 만큼(3-1=2)번 LOOP을
    돌며 전값을 기준으로 1씩 증가 시키면 0+1,0+2 가 되어 1,2 가
    RETURN 된다.
    다른 값도 마찬가지로 적용된다.
    위와 같은 방법으로 문제를 해결한다.

해법.
    단계1.위의 방법을 적용하기 위하여 가장먼저 해야 할 일은 바로
          전값을 해당 DATA에 MATCH 시키는 일이다.
          TABLE 을 읽을 때 ROWNUM을 붙여서 읽은 똑 같은 두개의
          DATASET을 아래와 같이 비교한다.

      SELECT  B.DATA DATA1,A.DATA DATA2
      FROM
         (SELECT ROWNUM RNUM,TO_NUMBER(DATA) DATA  
          FROM TEST37 WHERE DATA > ' ') A,
         (SELECT ROWNUM RNUM,TO_NUMBER(DATA) DATA  
          FROM TEST37 WHERE DATA > ' ') B
      WHERE A.RNUM(+) = B.RNUM - 1

      참고로 DATA COLUMN 에는 INDEX가 존재한다.
       그럴경우 결과는 아래와 같다.

       DATA1      DATA2    
       ---------- ----------
                0          
                3          0
                6          3
               10          6
               13         10

    단계2.다음으로 할 일은 위의 DATASET을 이용하여 두 값의 차이가
          1보다 큰 ROW 들을 찾아내는 일이다.
          위의 DATASET을 INLINE VIEW로 이용하여 다음과 같은
           QUERY를 만든다.

    SELECT DATA1,DATA2
    FROM (SELECT  B.DATA DATA1,A.DATA DATA2
          FROM
             (SELECT ROWNUM RNUM,TO_NUMBER(DATA) DATA  
              FROM TEST37 WHERE DATA > ' ') A,
             (SELECT ROWNUM RNUM,TO_NUMBER(DATA) DATA  
              FROM TEST37 WHERE DATA > ' ') B
          WHERE A.RNUM(+) = B.RNUM - 1) A
    WHERE A.DATA1 - A.DATA2 > 1

    결과는 아래와 같이 된다.

    DATA1      DATA2    
    ---------- ----------
             3          0
             6          3
            10          6
            13         10

    단계3.마지막으로 LOOP을 돌면서 1씩 증가시키는 과정이다.
          완벽한 LOOP을 지원하지 않지만 QUERY에서 LOOP이라는 것은
          결국 조인이다.
          FROM 절에 "(SELECT ROWNUM RNUM FROM USER_TABLES) B"를
          추가하고,
          WHERE 절에 "AND   B.RNUM < A.DATA1 - A.DATA2" 를
          추가한다.
          그리고 SELECT 절을 "TO_CHAR(DATA2+RNUM,'0000')"
          로 바꿔준다.
          그러면 최종 문장이 이렇게 된다.

          SELECT TO_CHAR(DATA2+RNUM,'0000') DATA
          FROM (SELECT  B.DATA DATA1,A.DATA DATA2
                FROM
                   (SELECT ROWNUM RNUM,TO_NUMBER(DATA) DATA  
                    FROM TEST37 WHERE DATA > ' ') A,
                   (SELECT ROWNUM RNUM,TO_NUMBER(DATA) DATA  
                    FROM TEST37 WHERE DATA > ' ') B
                WHERE A.RNUM(+) = B.RNUM - 1) A,
               (SELECT ROWNUM RNUM FROM USER_TABLES) B
          WHERE A.DATA1 - A.DATA2 > 1
          AND   B.RNUM < A.DATA1 - A.DATA2
      
          여기서 조건은 USER_TABLE에 있는 ROW수가 TEST37의 DATA가
          가지는 차이들의 최대값보다 크거나 같아야 한다는 것이다.
뒷풀이.
    전번 "빠진이빨찾기" 주제의 해법이 가졌던 한계를 보완하긴 했지만,
    위에서 언급했듯이 이번 해법도 한계를 안고 있다.
    충분히 많은 ROW를 가지는 TABLE을 새로 만들면 되지만
    그렇게 하지 않고 해법을 구하고자 한다면
    FROM절의  "(SELECT ROWNUM RNUM FROM USER_TABLES)" 부분을
    수정해야 한다.
    해당부분에서 UNION 이나 JOIN 을 이용해 충분히 많은 ROW를
    뒷받침 해줄 수 있도록 하는 것이 최선의 방법이 아닌가 한다.
번호 제목 글쓴이 날짜 조회 수
» 빠진이빨찾기 II 운영자 2002.09.18 3067
45 빠진이빨찾기 운영자 2002.09.18 3122
44 순환참조에서의 상위코드로집계 운영자 2002.09.18 3226
43 CONNECT BY의 SORT와 JOIN 운영자 2002.09.18 2811
42 REPORT 양식맞추기4 운영자 2002.09.18 2597
41 REPORT 양식 맞추기 III 운영자 2002.09.18 3015
40 REPORT 양식 맞추기 II 운영자 2002.09.18 2646
39 REPORT 양식 맞추기 운영자 2002.09.18 31359
38 1:1 JOIN / 1:M JOIN 운영자 2002.09.18 3000
37 TUNING-01 운영자 2002.09.18 2739
36 소계와 합계 운영자 2002.09.18 3489
35 중복DATA 찾아내기 운영자 2002.09.17 2817
34 GROUP별 DATA 분류 운영자 2002.09.17 2982
33 PARAMETER값에 따라 변경이 일어나는 문장 운영자 2002.09.17 15808
32 SELF JOIN 운영자 2002.09.17 3408
31 Outer Join 운영자 2002.09.17 2918
30 HINTS (출처-Oracle8.0 Tuning Guide) 운영자 2002.09.17 2735
29 단일행 문자 함수 운영자 2002.09.17 2977
28 단일행 수치 함수 운영자 2002.09.17 2869
27 NOT IN 의 함정 운영자 2002.09.17 2713
위로