메뉴 건너뛰기

tnt_db

Oracle 두개 RECORD를 한행에 DISPLAY 시키기

운영자 2002.09.18 14:05 조회 수 : 3100 추천:14

두개 RECORD를 한행에 DISPLAY 시키기

조건.
    SCOTT USER 로 LOGIN 하면 EMP TABLE 이 있다.

    EMPNO  ENAME   JOB       MGR     HIREDATE  SAL     COMM    DEPTNO    
    ------ ------- --------- ------- --------- ------- ------- -------
      7369 SMITH   CLERK        7902 80/12/17      800              20
      7499 ALLEN   SALESMAN     7698 81/02/20     1600     300      30
      7521 WARD    SALESMAN     7698 81/02/22     1250     500      30
      7566 JONES   MANAGER      7839 81/04/02     2975              20
      7654 MARTIN  SALESMAN     7698 81/09/28     1250    1400      30
      7698 BLAKE   MANAGER      7839 81/05/01     2850              30
      7782 CLARK   MANAGER      7839 81/06/09     2450              10
      7788 SCOTT   ANALYST      7566 82/12/09     3000              20
      7839 KING    PRESIDENT         81/11/17     5000              10
      7844 TURNER  SALESMAN     7698 81/09/08     1500       0      30
      7876 ADAMS   CLERK        7788 83/01/12     1100              20
      7900 JAMES   CLERK        7698 81/12/03      950              30
      7902 FORD    ANALYST      7566 81/12/03     3000              20
      7934 MILLER  CLERK        7782 82/01/23     1300              10

문제.
    EMP 에서 EMPNO와 ENAME 을 읽어서 보여주는데,
    한 행에 두개의 EMPNO 와 ENAME 이 나오도록 하는 것이 문제다.
    결과는 다음과 같으면 된다.

    EMP        ENAME      EMP        ENAME    
    ---------- ---------- ---------- ----------
          7369 SMITH            7499 ALLEN    
          7521 WARD             7566 JONES    
          7654 MARTIN           7698 BLAKE    
          7782 CLARK            7788 SCOTT    
          7839 KING             7844 TURNER    
          7876 ADAMS            7900 JAMES    
          7902 FORD             7934 MILLER    

생각.
    우리는 이미 ROW 형태의 DATA를 COLUMN 형식으로 바꾸는 문제를
    다뤄본 경험이 있다.
    비슷한 문제이다.
    빈도가 높게 질문이 오가는 문제 이기도 하다.
    각 RECORD에 번호표를 달아서 읽는 방법을 이용해 보자.
    1번과 2번 번호표가 붙은 놈은 1번째 줄에
    3번과 4번 번호표가 붙은 놈은 2번째 줄에 식으로
    DATA를 늘어 놓으면 된다.
    여기까지 얘기 했으면 벌써 감 잡은 사람이 많을 듯 하다.
    각각의 두개 ROW에 같은 KEY를 주어주고, 그 KEY 별로 GROUP BY 하게 되면
    두 RECORD는 한 줄에 나오게 된다.
    MAX(DECODE())를 써서 1.3.5.7.9 등은 앞에 2.4.6.8.10 등은 뒤에 놓으면
    한 줄에 보여 주는데 문제가 없다.
    그렇다면 KEY를 어떻게 주어 줄 것인가가 핵심이 된다.
    1번은 그대로,2번은 1번으로,3번은 그대로,4번은 3번으로 식으로
    RECORD에 붙은 번호표를 가공하면 결국 남게 되는 번호는
    1,3,5,7,9......식으로 홀수 번호만 2줄씩 남게 된다.
    1,2,3,4,5,6,7,8.......처럼 처음 읽혀온 번호가
    1,1,3,3,5,5,7,7.......과 같이 바뀌어 남게 된다.
    처음 번호표가 짝수이면 1을 빼주면 된다.
    홀수인 번호표에 1을 더해줘도 결과는 같다.
    우리는 홀수쪽을 이용하자.
    그렇다면 짝수는 어떻게 찾을 것인가?.
    ORACLE 함수중에 MOD 라는 함수가 있다.
    MOD(번호표,2) 하면 홀수는 1 짝수는 0을 RETURN한다.
    그렇게 하면 짝수인 번호에서 1을빼 홀수를 만드는데도 문제가 없다.
    그 번호표대로 GROUP BY 해주면 두 줄이 하나의 ROW로 뭉쳐진다.
    백문이 불여일견이라 했다.
    알았으면 직접 해보고, 설명이 장황해 모르겠으면 해법을 보자.

해법.
    단계1.번호표를 붙여서 DATA를 SELECT 하자    

    SELECT ROWNUM R_CNT,EMPNO,ENAME
    FROM   EMP

    R_CNT      EMPNO      ENAME    
    ---------- ---------- ----------
             1       7369 SMITH    
             2       7499 ALLEN    
             3       7521 WARD      
             4       7566 JONES    
             5       7654 MARTIN    
             6       7698 BLAKE    
             7       7782 CLARK    
             8       7788 SCOTT    
             9       7839 KING      
            10       7844 TURNER    
            11       7876 ADAMS    
            12       7900 JAMES    
            13       7902 FORD      
            14       7934 MILLER    
    단계2.위의 DATASET을 이용해 홀수는 그대로 짝수는 1을빼서 다시 보자.

    SELECT DECODE(MOD(R_CNT,2),0,R_CNT -1,R_CNT) 단위,
           EMPNO,ENAME
    FROM  (SELECT ROWNUM R_CNT,EMPNO,ENAME
           FROM   EMP)

    단위       EMPNO      ENAME    
    ---------- ---------- ----------
             1       7369 SMITH    
             1       7499 ALLEN    
             3       7521 WARD      
             3       7566 JONES    
             5       7654 MARTIN    
             5       7698 BLAKE    
             7       7782 CLARK    
             7       7788 SCOTT    
             9       7839 KING      
             9       7844 TURNER    
            11       7876 ADAMS    
            11       7900 JAMES    
            13       7902 FORD      
            13       7934 MILLER
    단계3.번호표에 MOD함수를 적용해 홀수면 앞에서고
          짝수면 뒤에서게 해보자.

    SELECT DECODE(MOD(R_CNT,2),0,R_CNT -1,R_CNT) 단위,
           DECODE(MOD(R_CNT,2),1,EMPNO) EMPNO,
           DECODE(MOD(R_CNT,2),1,ENAME) ENAME,
           DECODE(MOD(R_CNT,2),0,EMPNO) EMPNO,
           DECODE(MOD(R_CNT,2),0,ENAME) ENAME
    FROM  (SELECT ROWNUM R_CNT,EMPNO,ENAME
           FROM   EMP)

    단위       EMPNO      ENAME      EMPNO      ENAME    
    ---------- ---------- ---------- ---------- ----------
             1       7369 SMITH                          
             1                             7499 ALLEN    
             3       7521 WARD                            
             3                             7566 JONES    
             5       7654 MARTIN                          
             5                             7698 BLAKE    
             7       7782 CLARK                          
             7                             7788 SCOTT    
             9       7839 KING                            
             9                             7844 TURNER    
            11       7876 ADAMS                          
            11                             7900 JAMES    
            13       7902 FORD                            
            13                             7934 MILLER


    단계4.단위별로 GROUP BY를 한다.
          이때 우리에게 필요한것은 같은 단위에서 값이 존재하는
          COLUMN만을 가지고 오는 것이다.
          단위가 1인 경우를 예로 들면
    단위       EMPNO      ENAME      EMPNO      ENAME    
    ---------- ---------- ---------- ---------- ----------
             1       7369 SMITH                          
             1                             7499 ALLEN    
    
    두 RECORD를 1이라는 단위로 GROUP BY할때
    앞에서는 7369사번과 NULL 사번중 7396사번을 가져오고,
    뒤에서는 NULL 사번과 7499 사번중 7499 사번을 가져오면된다.
    어차피 각 COLUMN은 두 RECORD 중 한RECORD에 NULL 이 나올 수밖에
    없는 상황이다.
    우리는 이미 GROUP 함수는 NULL을 제외시키고 값을 구한다는 것도
    알고 있다.
    그중 MAX와 MIN은 반드시 수치함수가 아니라도 값을 비교하여
    답을 준다.
    그렇다면 GROUP BY 할때 MAX또는 MIN으로 값을 읽으면 될 것이다.
    해보자.

    SELECT  DECODE(MOD(R_CNT,2),0,R_CNT -1,R_CNT) 단위,
            MAX(DECODE(MOD(R_CNT,2),1,EMPNO)) EMP1,
            MAX(DECODE(MOD(R_CNT,2),1,ENAME)) ENAME1,
            MAX(DECODE(MOD(R_CNT,2),0,EMPNO)) EMP2,
            MAX(DECODE(MOD(R_CNT,2),0,ENAME))  ENAME2
    FROM
       (SELECT ROWNUM R_CNT,EMPNO,ENAME
        FROM   EMP)
    GROUP BY DECODE(MOD(R_CNT,2),0,R_CNT -1,R_CNT)

    이 부분에서 한가지 짚고 넘어가야할 부분이 있다면, 이 예제는
    EMP TABLE의 전체 DATA를 다루는 예제라는 것이다.
    혹 DATA가 읽혀오는 순서가 EMPNO 순서가 아니라면
    IN LINE VIEW 안에 WHERE 를 추가해 EMPNO 순서로 읽히도록
    할 수 있을 것이다. 물론 EMPNO가 PRIMARY KEY이기 때문이다.
    특정범위의 DATA만을 INDEX를 이용해 QUERY한 후 가공하는것 또한
    가능하다는 것이다.
    물론 DATA의 범위를 좁힌후의 가공이기에 INDEX SCAN을 한다는 것이다.

    결과는 예상대로 다음과 같이 나온다.

    단위       EMP1       ENAME1     EMP2       ENAME2    
    ---------- ---------- ---------- ---------- ----------
             1       7369 SMITH            7499 ALLEN    
             3       7521 WARD             7566 JONES    
             5       7654 MARTIN           7698 BLAKE    
             7       7782 CLARK            7788 SCOTT    
             9       7839 KING             7844 TURNER    
            11       7876 ADAMS            7900 JAMES    
            13       7902 FORD             7934 MILLER    

    단계5.이제 뭐가 남았는가?.
          제일 앞 자리에 버티고 있는 단위가 눈에 거슬린다. 살짝빼자.
          이렇게.

    SELECT  MAX(DECODE(MOD(R_CNT,2),1,EMPNO)) EMP,
            MAX(DECODE(MOD(R_CNT,2),1,ENAME)) ENAME,
            MAX(DECODE(MOD(R_CNT,2),0,EMPNO)) EMP,
            MAX(DECODE(MOD(R_CNT,2),0,ENAME)) ENAME
    FROM
       (SELECT ROWNUM R_CNT,EMPNO,ENAME
        FROM   EMP)
    GROUP BY DECODE(MOD(R_CNT,2),0,R_CNT -1,R_CNT)
  
    결과는 문제에서 요구한 그대로다.
    FULL SCAN을 사용했기에 PLAN은 생략하겟다.

뒷풀이.
    이렇게 하면 한줄에 두개가 아니라 세개 네개 그이상도 가능하다.
    유익하게 써먹을 수 있을 것이다.
    ROWNUM을 쓰지 않고는 방법이 없는가?.
    그렇지는 않다.
    ROWNUM 말고도 ROWNUM 과 같은 역할을 하게하는 방법이 있다.
    다음과 같은 QUERY를 만들어서 결과를 보자.

    SELECT A.EMPNO,A.ENAME,COUNT(B.EMPNO)
    FROM   EMP A,
           EMP B
    WHERE  A.EMPNO > 0
    AND    B.EMPNO <= A.EMPNO
    GROUP BY
           A.EMPNO,A.ENAME

    EMPNO ENAME      COUNT(B.EMPNO)
    ----- ---------- --------------
     7369 SMITH                   1
     7499 ALLEN                   2
     7521 WARD                    3
     7566 JONES                   4
     7654 MARTIN                  5
     7698 BLAKE                   6
     7782 CLARK                   7
     7788 SCOTT                   8
     7839 KING                    9
     7844 TURNER                 10
     7876 ADAMS                  11
     7900 JAMES                  12
     7902 FORD                   13
     7934 MILLER                 14

    결과는 해법에서 제시한 단계1과 같다.
    위의 결과를 해법에서 제시한 동일한 단계를 거쳐 같은 결과를 도출
    할 수 있다. 하지만 일단 문장의 간결성에서 해법에서 제시한 방법이
    우수하다.
    그뿐이 아니다.
    RECORD 건수가 증가할수록 그 차이는 극명하게 드러난다.
    피치못해 ROWNUM을 쓸수 없는 경우가 아니라면 해법에서 제시한
    방법을 사용하기 바란다.
위로