UNPIVOT 은 여러 열에 흩어진 값을 한 열로 모은다. 원래 어느 열에 있었는지는 새 이름 열에 적힌다.
SELECT *FROM 테이블UNPIVOT [INCLUDE NULLS | EXCLUDE NULLS] ( 값을 담을 새 열 FOR 원래 열 이름을 담을 새 열 IN (열1 [AS 값1], 열2 [AS 값2], …));
30번 부서 사원의 SAL 과 COMM 을 (항목, 값) 행으로 바꾼다.
SELECT EMPNO, ENAME, ITEM, VAL FROM (SELECT EMPNO, ENAME, SAL, COMM FROM EMP WHERE DEPTNO = 30) UNPIVOT (VAL FOR ITEM IN (SAL, COMM)) ORDER BY EMPNO, ITEM;
EMPNO ENAME ITEM VAL---------- ---------- ---- ---------- 7499 ALLEN COMM 300 7499 ALLEN SAL 1600 7521 WARD COMM 500 7521 WARD SAL 1250 7654 MARTIN COMM 1400 7654 MARTIN SAL 1250 7698 BLAKE SAL 2850 7844 TURNER COMM 0 7844 TURNER SAL 1500 7900 JAMES SAL 950
사원 6명 × 2열 = 12행이 될 것 같지만 10행이다. BLAKE 와 JAMES 의 COMM 은 NULL 이라 행이 만들어지지 않았다.
기본값은 EXCLUDE NULLS 다. TURNER 의 COMM 0 은 NULL 이 아니라 남는다.
ITEM 열에는 원래 열 이름이 문자로 들어간다. IN (SAL AS 'SAL', COMM AS 'COMM') 처럼 AS 로 바꿔 넣을 수도 있다.
IN 목록의 열은 자료형이 같아야 한다. IN (SAL, ENAME) 처럼 숫자와 문자를 섞으면 ORA-01790 이 났다.
SELECT EMPNO, ENAME, ITEM, VAL FROM (SELECT EMPNO, ENAME, SAL, COMM FROM EMP WHERE DEPTNO = 30) UNPIVOT INCLUDE NULLS (VAL FOR ITEM IN (SAL, COMM)) ORDER BY EMPNO, ITEM;
EMPNO ENAME ITEM VAL---------- ---------- ---- ---------- 7499 ALLEN COMM 300 7499 ALLEN SAL 1600 7521 WARD COMM 500 7521 WARD SAL 1250 7654 MARTIN COMM 1400 7654 MARTIN SAL 1250 7698 BLAKE COMM 7698 BLAKE SAL 2850 7844 TURNER COMM 0 7844 TURNER SAL 1500 7900 JAMES COMM 7900 JAMES SAL 950
위 PIVOT 결과를 WITH 절에 두고 UNPIVOT 하면 세로 모양으로 돌아온다. D10 AS 10 처럼 열 이름 대신 원래 부서 번호를 넣는다.
WITH P AS (SELECT * FROM (SELECT JOB, DEPTNO, SAL FROM EMP) PIVOT (SUM(SAL) FOR DEPTNO IN (10 AS D10, 20 AS D20, 30 AS D30))) SELECT * FROM P UNPIVOT (SAL FOR DEPTNO IN (D10 AS 10, D20 AS 20, D30 AS 30)) ORDER BY JOB, DEPTNO;
PIVOT 절이 없던 시절에는 CASE 나 DECODE 로 열마다 조건을 걸고, 바깥을 집계 함수로 감쌌다. 지금도 다른 DBMS 로 옮기기 쉬운 방법이다.
SELECT JOB, SUM(CASE WHEN DEPTNO = 10 THEN SAL END) AS D10, SUM(CASE WHEN DEPTNO = 20 THEN SAL END) AS D20, SUM(CASE WHEN DEPTNO = 30 THEN SAL END) AS D30 FROM EMP GROUP BY JOB ORDER BY JOB;
UNPIVOT : 여러 열에 흩어진 값을 한 열로 모아 행으로 내리는 절. PIVOT 의 반대 방향이다.
FOR 열 IN (…) : PIVOT 에서 펼칠 열과 만들 열 목록, UNPIVOT 에서 행으로 내릴 열 목록을 정하는 부분.
암묵적 GROUP BY : PIVOT 이 집계·FOR 에 쓰이지 않은 나머지 열을 묶음 기준으로 삼는 동작.
INCLUDE NULLS : UNPIVOT 에서 값이 NULL 인 칸도 행으로 남기는 옵션. 기본은 EXCLUDE NULLS 다.
크로스탭 : 행과 열 양쪽에 분류 기준을 두고 칸에 집계를 넣은 표. PIVOT 결과가 이 모양이다.
DECODE : DECODE(식, 값, 결과, …) 로 값이 같을 때의 결과를 고르는 Oracle 함수. 일치하는 값이 없으면 NULL 이다.
📝 연습 문제
선택지를 클릭하면 정답·해설이 펼쳐집니다.
문제 1
SELECT COUNT(*) FROM EMP PIVOT (SUM(SAL) FOR DEPTNO IN (10 AS D10, 20 AS D20, 30 AS D30)); 의 결과는? (EMP 는 14명이고 직무는 5가지다)
문제 2
SELECT * FROM (SELECT JOB, DEPTNO, SAL FROM EMP) PIVOT (SUM(SAL) AS TOT FOR DEPTNO IN (10 AS D10, 20)); 의 결과 열 이름을 순서대로 고르면?
문제 3
WITH P AS (SELECT * FROM (SELECT JOB, DEPTNO, SAL FROM EMP) PIVOT (SUM(SAL) FOR DEPTNO IN (10 AS D10, 20 AS D20, 30 AS D30))) SELECT COUNT(*) FROM P UNPIVOT (SAL FOR DEPTNO IN (D10, D20, D30)); 의 결과는? (P 는 5행 × 부서 3열이고, 그중 6칸이 NULL 이다)
문제 4
PIVOT 절 없이 직무별로 10·20·30번 부서의 급여 합계를 열로 펴려고 한다. 알맞은 방법은?
문제 5
SELECT * FROM (SELECT DEPTNO, JOB FROM EMP) PIVOT (COUNT(*) FOR JOB IN ('SALESMAN' AS S)) ORDER BY DEPTNO; 에서 10번 부서 행의 S 값은? (10번 부서에는 SALESMAN 이 없다)