COLUMN을 ROW 형태로 변환방법 정리

Updated:


1. 개요

앞장에서는 ROW를 COLUMN으로 변환하는 방법을 설명하였습니다. 이번에는 역으로 COLUMN형태를 ROW형태로 변환하는것을 설명하겠습니다. 칼러형태로 수평적으로 연결된 데이타를 레코드 형태로 수직으로 나열되어 있는 데이타로 변환한다는 이야기입니다. 오라클의 UNPIVOT, 테이블 복제와 DECODE/CASE사용방법, UNION ALL, ROLLUP 등의 방법이 있습니다.

가. 예제 테이블

1) EMP 테이블

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

2) DEPT 테이블

DEPTNO DNAME LOC
10 ACCOUNTING} NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON

2. 사용예제

가. UNPIVOT 사용

이 방법은 오라클 11g 이상 버젼에서 사용가능한 방법입니다. 가장 간단한 방법중 하나로 속도도 좋은 장점을 가지고 있습니다.

아래의 1) SQL에서는 unpivot_test 테스트 테이블을 생성하고 테스트 한 사례입니다. Customer_id별로 A, B, C, D 칼럼을 가지고 있는데 각 칼럼별로 점수가 나와 있는 형태입니다. 이것을 결과값처럼 수직적인 레코드형태로 변환하라는 것이 요구사항입니다.

UNPIVOT 함수를 사용하여 아래의 1번 SQL처럼 사용할수 있습니다. quantity, product_code 칼럼명을 만들고 그에 대응하는 값들을 IN절로 처리하였습니다. 이때 처리되는 대상의 종류와 갯수를 알고 있어야 가능한 방법입니다.

1) SQL
SELECT *
FROM   unpivot_test
UNPIVOT (quantity FOR product_code IN (a AS 'A', b AS 'B', c AS 'C', d AS 'D'));

- 실행계획
-------------------------------------------------------------------------
| Id  | Operation           | Name         | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |              |    16 |   672 |     5   (0)|
|*  1 |  VIEW               |              |    16 |   672 |     5   (0)|
|   2 |   UNPIVOT           |              |       |       |            |
|   3 |    TABLE ACCESS FULL| UNPIVOT_TEST |     4 |    68 |     3   (0)|
-------------------------------------------------------------------------           
  • unpivot_test 테이블
ID CUSTOMER_ID A B C D
1 101 10 20 30  
2 102 40   50  
3 103 60 70 80 90
4 104 100      
  • 결과값
ID CUSTOMER_ID PRODUCT_CODE QUNTITY
1 101 A 10
1 101 B 20
1 101 C 30
2 102 A 40
2 102 C 50
3 103 A 60
3 103 B 70
3 103 C 80
3 103 D 90
4 104 A 100


나. UNION ALL, 테이블 복제 사용

이번에는 가장 간단한 방법인 UNION ALL을 사용하는 방법과 테이블 복제를 통해 변환하는 방법을 알아보겠습니다. 예제 테이블이 동일하여 2개의 방법을 동시에 설명하도록 하겠습니다.

UNION ALL을 사용한 방법은 1번과 같으며 동일 테이블에 4번 접근하여 처리하는 방법입니다. 그래서 SQL 속도는 가장 좋지않은 방법으로 판단됩니다.

2번 SQL의 경우 테이블 복제기법을 사용하여 테이블에 1번만 접근하고 오라클 메모리에서 카테시안 프로덕트를 발생시켜 복제 테이블을 3개 더 만들어 처리하는 방법입니다. 많이 사용하는 SQL형태인데 주의점은 복제하는 데이타의 량이 많은 경우 성능장애를 발생시킬수 있으므로 주의하셔야 합니다. 보통 몇천건이하로만 사용하는 것을 권장드리며 데이타 량이 많은 경우에는 UNPIVOT이나 ROLLUP 복제를 사용하기를 바랍니다.

2번 SQL의 경우 CONNECT BY LEVEL 함수를 많이 사용합니다. 인라인뷰의 LEVEL이 1, 2, 3, 4 라는 숫자를 반환하는데 바깥쪽 테이블 TEST11 과 WHERE절 연결조건이 없습니다. 이런경우 MERGE JOIN CARTESIAN 이 발생되며 테이블 복제가 발생되며 CNT 즉 FLAG를 이용하여 DECODE, CASE처리하면 우리가 원하는 값들을 추출할수 있습니다.

1) UNION ALL SQL

SELECT COLL,DEPT,'1학년', FRE
FROM TEST11
UNION ALL
SELECT COLL,DEPT,'2학년',SUP
FROM TEST11
UNION ALL
SELECT COLL,DEPT,'3학년',JUN
FROM TEST11
UNION ALL
SELECT COLL,DEPT,'4학년',SEN
FROM TEST11

- 실행계획
------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Bytes | Cost (%CPU)|
------------------------------------------------------------------
|   0 | SELECT STATEMENT   |        |    48 |  1200 |    12   (0)|
|   1 |  UNION-ALL         |        |       |       |            |
|   2 |   TABLE ACCESS FULL| TEST11 |    12 |   300 |     3   (0)|
|   3 |   TABLE ACCESS FULL| TEST11 |    12 |   300 |     3   (0)|
|   4 |   TABLE ACCESS FULL| TEST11 |    12 |   300 |     3   (0)|
|   5 |   TABLE ACCESS FULL| TEST11 |    12 |   300 |     3   (0)|
------------------------------------------------------------------

2) 테이블 복제 SQL 
SELECT *
FROM TEST11,  (SELECT LEVEL CNT 
               FROM DUAL
               CONNECT BY LEVEL < 5);

3) 테이블 복제 완성 SQL
SELECT COLL, DEPT, DECODE(CNT,1,'1학년',2,'2학년',3,'3학년',4,'4학년') KEY3,
       DECODE(CNT,1,FRE,2,SUP,3,JUN,4,SEN) JUMSU
FROM TEST11,   (SELECT LEVEL CNT 
                FROM DUAL
                CONNECT BY LEVEL < 5);

- 실행계획
------------------------------------------------------------------------------
| Id  | Operation                      | Name   | Rows  | Bytes | Cost (%CPU)|
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |        |    12 |   600 |     5   (0)|
|   1 |  MERGE JOIN CARTESIAN          |        |    12 |   600 |     5   (0)|
|   2 |   VIEW                         |        |     1 |    13 |     2   (0)|
|*  3 |    CONNECT BY WITHOUT FILTERING|        |       |       |            |
|   4 |     FAST DUAL                  |        |     1 |       |     2   (0)|
|   5 |   BUFFER SORT                  |        |    12 |   444 |     5   (0)|
|   6 |    TABLE ACCESS FULL           | TEST11 |    12 |   444 |     3   (0)|
------------------------------------------------------------------------------

  • 원 테이블 (test11)
COL DEPT FRE SUP JUN SEN
공과대학 항공우주공학과 120 140 140 120
공과대학 기계공학과 240 260 240 240
공과대학 조선공학과 118 138 136 126
공과대학 산업공학과 123 141 140 120
공과대학 전자공학과 126 141 140 128
공과대학 재료공학과 128 132 142 126
공과대학 화학공학과 122 141 141 122
공과대학 전기공학과 120 140 140 120
공과대학 컴퓨터공학과 124 145 142 121
공과대학 섬유공학과 126 144 140 120
공과대학 자동화공학과 122 142 144 125
공과대학 고분자공학과 121 142 143 124
  • 최종값
COLL DEPT KEY3 JUMSU
공과대학 항공우주공학과 1학년 120
공과대학 기계공학과 1학년 240
공과대학 조선공학과 1학년 118
공과대학 산업공학과 1학년 123
공과대학 전자공학과 1학년 126
공과대학 재료공학과 1학년 128
공과대학 화학공학과 1학년 122
공과대학 전기공학과 1학년 120
공과대학 컴퓨터공학과 1학년 124
공과대학 섬유공학과 1학년 126
공과대학 자동화공학과 1학년 122
공과대학 고분자공학과 1학년 121
공과대학 항공우주공학과 2학년 140
공과대학 기계공학과 2학년 260
…… 생략    
공과대학 전기공학과 4학년 120
공과대학 컴퓨터공학과 4학년 121
공과대학 섬유공학과 4학년 120
공과대학 자동화공학과 4학년 125
공과대학 고분자공학과 4학년 124
  • 원테이블을 3번 더 복제한 경우(MERGE JOIN CARTESIAN )
COL DEPT FRE SUP JUN SEN CNT
공과대학 항공우주공학과 120 140 140 120 1
공과대학 기계공학과 240 260 240 240 1
공과대학 조선공학과 118 138 136 126 1
공과대학 산업공학과 123 141 140 120 1
공과대학 전자공학과 126 141 140 128 1
공과대학 재료공학과 128 132 142 126 1
공과대학 화학공학과 122 141 141 122 1
공과대학 전기공학과 120 140 140 120 1
공과대학 컴퓨터공학과 124 145 142 121 1
공과대학 섬유공학과 126 144 140 120 1
공과대학 자동화공학과 122 142 144 125 1
공과대학 고분자공학과 121 142 143 124 1
….            
공과대학 전기공학과 120 140 140 120 2
공과대학 컴퓨터공학과 124 145 142 121 2
공과대학 섬유공학과 126 144 140 120 2
공과대학 자동화공학과 122 142 144 125 2
….            
공과대학 전기공학과 120 140 140 120 4
공과대학 컴퓨터공학과 124 145 142 121 4
공과대학 섬유공학과 126 144 140 120 4
공과대학 자동화공학과 122 142 144 125 4
공과대학 고분자공학과 121 142 143 124 4


다. ROLLUP 사용

이 방법은 ROLLUP을 연구하다 우연히 발견하게 된 방법입니다. 아래의 1번 SQL에서 ROLLUP((COLL,DEPT,FRE,SUP,JUN,SEN),0,1,2) 항목을 보게되면 SQL을 만드는데 필요한 칼럼들을 Composite Colmun 형태로 ( ) 으로 묶고 그 뒤에다 원하는 복제 테이블 숫자만큼 0, 1, 2 숫자를 넣으면 됩니다. 숫자를 3개 넣었으므로 복제가 3번 더 일어납니다. 이 숫자들은 grouping(0), grouping(1), grouping(2)라는 의미로 rollup 안에서 마법과 같은 현상을 일으켜 데이블 복제를 발생시키는 것입니다. 주의점은 숫자를 1개씩 올려줘야 합니다. 역시 테이블에 1번만 접근하여 처리하였습니다.

1) ROLLUP 복제
SELECT COLL,DEPT,FRE,SUP,JUN,SEN, MIN(FRE), 
           GROUPING_ID(COLL,0,1,2) A
FROM TEST11
GROUP BY ROLLUP((COLL,DEPT,FRE,SUP,JUN,SEN),0,1,2)
ORDER BY GROUPING_ID(COLL,0,1,2)


2) ROLLUP 완성문장
SELECT COLL, DEPT,  DECODE(A,0,'1학년',1,'2학년',3,'3학년  ',7,'4학년') KEY3,  
           DECODE(A,0,FRE,1,SUP,3,JUN,7,SEN) JUMSU
FROM (SELECT COLL,DEPT,FRE,SUP,JUN,SEN, MIN(FRE), GROUPING_ID(COLL,0,1,2) A
          FROM TEST11
          GROUP BY ROLLUP((COLL,DEPT,FRE,SUP,JUN,SEN),0,1,2)
          ORDER BY GROUPING_ID(COLL,0,1,2)  )
WHERE COLL IS NOT NULL;

- ROLLUP 실행계획
-----------------------------------------------------------------------
| Id  | Operation               | Name   | Rows  | Bytes | Cost (%CPU)|
-----------------------------------------------------------------------
|   0 | SELECT STATEMENT        |        |    12 |  1068 |     5  (40)|
|   1 |  VIEW                   |        |    12 |  1068 |     5  (40)|
|   2 |   SORT ORDER BY         |        |    12 |   444 |     5  (40)|
|*  3 |    FILTER               |        |       |       |            |
|   4 |     SORT GROUP BY ROLLUP|        |    12 |   444 |     5  (40)|
|   5 |      TABLE ACCESS FULL  | TEST11 |    12 |   444 |     3   (0)|
-----------------------------------------------------------------------


3. 결언

이번에는 COLUMN을 ROW형태로 변환하는 방법을 정리해보았습니다. 코로나 사태가 오늘보니 확진자가 많이 줄었네요. 빨리 정상적인 일상생활을 하고싶습니다. 건강 조심하시기 바랍니다.