메뉴 건너뛰기

tnt_db

Oracle 빠진이빨찾기 III

운영자 2002.09.18 13:31 조회 수 : 3359 추천:10

조건.
    VAL1      
    ----------
    A97      
    B10      
    B12  

    TEST38 에 위와 같은 DATA 가 있다.

문제.
    이미 빠진이빨찾기I 에서 다루었던 내용의 반복이다.
    단지 이번 문제가 I과 다른점은 숫자로만 이루어진 값이
    아니라는 것이다.
    문제는 A00 에서 부터 Z99 까지의 값을 가질수 있다.
    위의 값을 보면 MAX 값이 B12 이다.
    해야할 일은 A00에서 B12 까지의 값중에서 TEST38 에서 갖고 있는
    값을 제외한 나머지 값을 찾아내는 것이다.
    A00 ~ A99 중 A97을 제외한 99개의 값과 B00 에서 B12중 B10과 B12를
    제외한 11개의 값을 합친 총 110개의 값을 찾아내면 된다.
    문자형태가 들어가는 것은 첫번째 자리에만 국한되며,뒤의 두자리는
    숫자 형태의 일련값이 들어 간다.
    즉 A99 뒤에 B00 이 온다는 것이다.

    VAL1
    ----------
    A00      
    A01      
    중략
    A96      
    A98      
    A99      
    B00      
    중략
    B07      
    B08      
    B09      
    B11      
    110 행이 선택되었습니다
  
    결과가 위와 같이 나오면 된다.
    빠진이빨찾기I 에서 사용한 방법을 사용한다.

생각.
    문제를 해결하는 방법은 이미 빠진이빨찾기I 에서  다루었다.
    다만 문자가 들어있어,단순히 숫자형태로만 들어있던 전번 경우와
    다른점이 있다.
    이 부분을 어떻게 해결하느냐가 문제의 열쇠가 될 것이다.
    먼저 생각해 볼 수 있는 것은 문자가 들어있는 값을 숫자로
    치환하여 일련의 값을 만든 후
    다시 그 일련의 값들을 문자로 바꾸어 보여주는 방법을 생각해
    볼 수 있다.
    그렇지 않다면,
    A 에 특정값을 더하면 B 를 RETURN 하는 함수를 만들어
    사용할 수도 있다.
    하지만 우리는 ORACLE 자체에서 제공하는 함수 만을 사용하여
    문제를 풀어보자.
    ORACLE 함수에는 ASCII() 라는 함수와 CHR() 이라는
    함수가 있다.
    ASCII() 는 주어진 문자의 ASCII 값을 RETURN 하며,
    CHR() 는 주어진 ASCII 값에 해당하는 문자를 RETURN 한다.
    이 정도만 알고 있다면 문제의 50%는 해결된것이나 다름없다.
    이미 3자리 코드중 제일 앞 자리만이 A~Z 값을 가지며,
    나머지 두자리는 숫자형태라는 것을 알고 있다.
    코드는 A00 부터 시작한다는 것도 이미 알고있다.
    A~Z까지가 결국은 100단위 이상의 자리값을 결정짓는다.
    A는 100보다 작은 값 ,B는 100,C는 200......
    과 같은 형태일 것이다.
    결국 B를 100으로 치환하면 최대값은 100+12 가 된다.
    112의 일련번호를 0번부터 만들어주는 일은 어렵지
    않을 것이다.
    만약 최대값이 C로 시작되는 코드였다면 200번대의 번호로
    치환이 되었을 것이다.
    이렇게 일련번호를 읽어놓고 그값을 계산해서 100보다 작으면 'A',
    100번대이면 'B',200번대이면 'C' 를 첫번째 자리에 놓고 뒤에
    100으로 나눈 나머지 값을 붙여주면 A00 에서 최대 코드까지의
    일련의 코드가 만들어진다.
    이중에서 TEST38에 존재하고 있는 DATA만 제외시키면
    빠진이빨이다.
    
해법.
    단계1.SELECT ASCII('A') FROM DUAL
          위의 문장을 실행하면 65 라는 숫자를 RETURN 한다.
          즉, 'A'의 ASCII 값이 65라는 얘기다.
          그렇다면 최대코드의 첫번째 문자를 ASCII() 함수에
          적용시킨후 그 값에서 65를 빼주면
          'A' 는 0,'B' 는 1,'C' 는 2.....와 같은 값을 RETURN 한다.
          다시 그 값에 100을 곱한 후 최대코드의 뒷부분 2자리를 더하면
          최대코드값을 숫자로 치환한 값이 나온다.
          이값은 우리가 문제를 해결하기 위해 우리의 규칙대로
          치환한 값이다.
          뒤에 동일 규칙을 역으로 적용하여 다시 문자로 만들어
          줄 것이다.
          문장을 구성하면 아래와 같이 된다.

          SELECT (ASCII(SUBSTRB(MAX(VAL1),1,1)) - 65) * 100  +  
                 TO_NUMBER(SUBSTRB(MAX(VAL1),2))
          FROM   TEST38

          결과는 112 가 된다.
          112라는 값은 'B12'의 숫자 치환 값이며 우리가 필요로하는
          일련번호의 최대 값이기도 하다.
    단계2.두번째로 할일은 단계1의 정보를 이용하여 영 부터 113개의
          번호를 채취하는 일이다.

          SELECT  ROWNUM-1 NO
          FROM    USER_TABLES
          WHERE   ROWNUM-1 <= (SELECT (ASCII(SUBSTRB(MAX(VAL1),1,1))
                                             - 65) * 100  +  
                             TO_NUMBER(SUBSTRB(MAX(VAL1),2))
                             FROM TEST38)
          결과는 0 에서 부터 112 까지 113개의 ROW의 번호가 읽혀온다.          
    단계3.이번 단계에서는 각 번호의 첫번째 자리를 문자로 바꿔 준다.
          단계2의 문장을 INLINE VIEW 로 하여 다음과 같이 SELECT 한다.
          CHR(65 + FLOOR(NO/100)) || LPAD(TO_CHAR(MOD(NO,100)),2,'0')
          단계2의 정보가 첫번째 자리가 문자인 형태로 바뀐다.
          그리고 MINUS 집합 연산자를 이용해 TEST38에 있는 DATA를
          제외시키면 최종 문장은 아래와 같이 된다.    
      
          SELECT  CHR(65 + FLOOR(NO/100)) ||
                  LPAD(TO_CHAR(MOD(NO,100)),2,'0') VAL
          FROM   (SELECT  ROWNUM-1 NO
                  FROM    USER_TABLES
                  WHERE   ROWNUM-1 <=
                         (SELECT (ASCII(SUBSTRB(MAX(VAL1),1,1))-65)
                                  * 100  +  
                                 TO_NUMBER(SUBSTRB(MAX(VAL1),2))
                          FROM   TEST38))
          MINUS
          SELECT VAL1 FROM TEST38

          물론 결과는 문제에 있는 답과 동일하다.
위로