- 전체
- Sample DB
- database modeling
- [표준 SQL] Standard SQL
- G-SQL
- 10-Min
- ORACLE
- MS SQLserver
- MySQL
- SQLite
- postgreSQL
- 데이터아키텍처전문가 - 국가공인자격
- 데이터 분석 전문가 [ADP]
- [국가공인] SQL 개발자/전문가
- NoSQL
- hadoop
- hadoop eco system
- big data (빅데이터)
- stat(통계) R 언어
- XML DB & XQuery
- spark
- DataBase Tool
- 데이터분석 & 데이터사이언스
- Engineer Quality Management
- [기계학습] machine learning
- 데이터 수집 및 전처리
- 국가기술자격 빅데이터분석기사
- 암호화폐 (비트코인, cryptocurrency, bitcoin)
ORACLE PIVOT 쿼리 - 가로세로변환, ROW 단위 자료를 COLUMN 단위 자료로 변환2(예제1)
2020.06.12 17:26
PIVOT 쿼리 - 가로세로변환, ROW 단위 자료를 COLUMN 단위 자료로 변환2(예제1)
1.
SCOTT/TIGER 유저로 로그인 한다.
아래는 emp 테이블에 있는 데이타들이다.
EMPNO ENAME DEPTNO
-----------------------
7369 SMITH 20
7499 ALLEN 30
7521 WARD 30
7566 JONES 20
7654 MARTIN 30
7698 BLAKE 30
7782 CLARK 10
7788 SCOTT 20
7839 KING 10
7844 TURNER 30
7876 ADAMS 20
7900 JAMES 30
7902 FORD 20
7934 MILLER 10
위 테이블에서, 한 row는 해당 deptno에 속하는 한 명의 사원(employee)을 나타낸다.
이 테이블에서 각 deptno에 속하는 사원의 수를 다음과 같이 출력하고자 한다면,
DEPTNO_10 DEPTNO_20 DEPTNO_30
-------------------------------
3 5 6
쿼리를 아래와 같이 만들어 준다.
SELECT COUNT (DECODE (deptno, 10, 1)) deptno_10,
COUNT (DECODE (deptno, 20, 1)) deptno_20,
COUNT (DECODE (deptno, 30, 1)) deptno_30
FROM emp
| * 위의 쿼리는 다른 방법보다 제일 좋은 것으로 보임.
우선 deptno_10,deptno_20,deptno_30 이 가로로 나와야 하므로 쿼리는 SELECT ...deptno_10, ...deptno_20, ...deptno_30 FROM EMP 와 같은 형식이어야 할 것을 생각해야 함. 그런데 나와야 하는 데이타 값들이 모두 갯수임. 그렇다면 그냥 COUNT 만 해도 될 것. 한 라인 지날 때 COUNT 함수가 작동하게만 하면 됨. COUNT(1) 이나 COUNT(777) 이나 전부 1임. 그냥 한 행을 COUNT 했다는 것.
COUNT 는 지정한 열의 행이 몇 개인지를 리턴하므로 COUNT (DECODE (deptno, 10, 1)) 하면 DEPTNO 라는 열의 값이 10 일 때 몇개의 행인지 리턴하게 됨.
일단 전체 데이타를 보자. SELECT ROWNUM, T.* 을 해 보면 아래와 같이 나옴.
ROWNUM 이 1인 행부터 보면 여기서 DEPTNO 가 10 이나, 20 이나, 30 일 때만 COUNT 하면 됨.
SELECT COUNT (DECODE (deptno, 10, 1)) deptno_10,
이 쿼리가 제일 간단한 것임.
* 또 다른 방법 *********************************************************** DEPTNO_10 DEPTNO_20 DEPTNO_30 와 같은 모양을 만들기 위해 먼저 DEPTNO_10 이 3개, DEPTNO_20 이 5개, DEPTNO_30 이 6개임을 아래 쿼리로 구한다.
SELECT DEPTNO, COUNT(EMPNO) A 위를 실행하면 RESULTSET 은 아래처럼 나온다.
DEPTNO 가 세로로 나오는데 이걸 가로로 나오도록 하려면 대략 SELECT ... AS DEPTNO_10, ... AS DEPTNO_20, ... AS DEPTNO_30 FROM ( SELECT DEPTNO,COUNT(EMPNO) A ) 의 형태가 되어야 함. 따라서 일단 아래와 같은 생각을 해 본다(대각선 모양을 만들기 위한 과정임) - 위 RESULTSET 으로부터 가로로 변환을 시켜야 한다. - 위 RESULTSET 처럼 ROW 는 3개로 놔두되 컬럼도 3개로 일단 아래처럼 만들어야 한다. DEPTNO_10 DEPTNO_20 DEPTNO_30 ------------------------------- NULL 5 NULL ------------------------------- 3 NULL NULL ------------------------------- - 각 라인마다 실제 데이타는 하나이고 나머지는 NULL 이므로 DECODE 를 이용해야 함을 생각한다. - 쿼리는 아래처럼 만들면 됨. SELECT DECODE(DEPTNO, 10, A, NULL) deptno_10, 이 쿼리를 실행하면 아래와 같이 나옴.
- 이제 3개 라인을 하나의 라인으로 줄이기 위해 MIN 이나 MAX 함수를 쓰면 된다. - null 이 들어 있는 컬럼은 어떤 함수에도 연산을 하지 않으므로 min 또는 max 함수를 쓰는 것. 그룹함수중 자료형에 무관하게 쓸 수 있는 함수로는 MAX 와 MIN 함수가 있는데, 어느 것을 적용해 도 NULL 값을 제외하면 C1 부터 C4 까지 각 컬럼 별로 하나의 행만 통과되므로 결과는 동일하다.
SELECT MAX(DECODE(DEPTNO, 10, A, NULL)) deptno_10, 결과는 아래와 같다.
|
2.
SCOTT/TIGER 유저로 로그인 한다.
아래와 같이 deptno 별로 clerk, salesman, manager의 수가 나타나도록 쿼리를 만들어보자.
(여기서도 emp 테이블을 사용한다.)
DEPTNO CLERK SALESMAN MANAGER
---------- ---------- ---------- ----------
10 1 0 1
20 2 0 1
30 1 4 1
쿼리는 아래와 같다.
SELECT deptno
, COUNT (DECODE (job, 'CLERK', 1)) clerk
, COUNT (DECODE (job, 'SALESMAN', 1)) salesman
, COUNT (DECODE (job, 'MANAGER', 1)) manager
FROM emp
GROUP BY deptno
| * 위의 쿼리는 다른 방법보다 제일 좋은 것으로 보임.
우선 DEPTNO ,clerk ,salesman, manager 가 가로로 나와야 하므로 쿼리는 SELECT ...DEPTNO , ... clerk , ... salesman, ... manager FROM EMP 와 같은 형식이어야 할 것을 생각해야 함. 그런데 나와야 하는 데이타 값들이 모두 갯수임. 그렇다면 그냥 COUNT 만 해도 될 것. 한 라인 지날 때 COUNT 함수가 작동하게만 하면 됨. COUNT(1) 이나 COUNT(777) 이나 전부 1임. 그냥 한 행을 COUNT 했다는 것.
COUNT 는 지정한 열의 행이 몇 개인지를 리턴하므로 COUNT (DECODE (deptno, 10, 1)) 하면 DEPTNO 라는 열의 값이 10 일 때 몇개의 행인지 리턴하게 됨.
일단 전체 데이타를 보자. SELECT ROWNUM, T.* 을 해 보면 아래와 같이 나옴.
ROWNUM 이 1인 행부터 보면 여기서 DEPTNO 는 그냥 뽑아 올 수 있는 것이고 JOB 이 무엇이냐만 DECODE 로 나눠주면서 COUNT 하면 됨. 결국 SELECT deptno 이 쿼리가 제일 간단한 방법.
* 또 다른 방법 DEPTNO CLERK SALESMAN MANAGER
원래는 JOB 이라는 필드 안에 세로로 쫙 들어 있던 것들을 위와 같이 3개의 필드로 나눠야 하므로 CEIL 과 MOD ,DECODE 함수를 사용해 볼 생각을 해 본다. 일단 아래의 쿼리로 CLERK SALESMAN MANAGER 를 새로운 컬럼으로 만들어 보자. SELECT ... DEPTNO, ... CLERK, ... SALESMAN, ... MANAGER FROM ( ... ) 와 같은 형식이 될 것. 일단 전체 데이타를 보자.
SELECT ROWNUM, T.*
을 해 보면 아래와 같이 나옴.
일단 첫 컬럼을 DEPTNO 로 해야 하니 DEPTNO 로 GROUP BY 한 후 나머지들을 COUNT 해 볼 생각을 해 보자. 쿼리 던져 보니 그냥 ORDER BY 하면 어찌해야 할지 보인다.
SELECT *
위 쿼리 던지면 RESULTSET 은 아래와 같다.
이제 필요한건, DEPTNO 과 JOB 필드 뿐이고 JOB 필드만 세로로 되어 있는걸(=하나의 필드에 있는걸) 여러개의 필드로 만들면 된다.
SELECT DISTINCT DEPTNO, JOB,
해보면 아래와 같다.
그런데 찾아야 하는 필드는 DEPTNO, CLERK , MAMAGER , SALESMAN 뿐이므로 ANALYST 와 PRESIDENT 는 뺀다. 아래 쿼리 실행시킨다.
SELECT DISTINCT DEPTNO, JOB, COUNT(JOB) OVER(PARTITION BY DEPTNO, JOB ORDER BY DEPTNO ) CNT
결과는 아래와 같다.
여기서도 COUNT 를 쓰면 되겠으나 지금 그렇게 구하자는게 아니므로 아래와 같이 한다.
SELECT DEPTNO, ) 이렇게 하면 아래와 같이 나온다.
다시 GROUP BY 하고 MAX 로 합친다. 그룹함수중 자료형에 무관하게 쓸 수 있는 함수로는 MAX 와 MIN 함수가 있는데, 어느 것을 적용해도 NULL 값을 제외하면 C1 부터 C4 까지 각 컬럼 별로 하나의 행만 통과되므로 결과는 동일하다.
SELECT DEPTNO,
이렇게 하면 아래와 같이 잘 나온다.
** 그냥 헛 지랄 해 본 거임.
|
3.
daniel 유저로 로그인한다.
SELECT *
FROM SAM_TAB02 를 해 보면 아래와 같다.
별 의미 없이 F107 에서 F125 까지 들어가 있다(아래 캡쳐화면은 약간 짤린 것)

하나의 필드로 이루어진 위의 데이타를 아래 그림처럼 5개의 필드에 나눠 담는 방법에 대해 알아본다.
맨 앞의 RNO 는 원래 있던 데이타가 아니므로 사실상 4개의 필드에 담는 것.
이렇게 필드의 갯수가 정해져 있을 때는 CEIL 과 MOD 함수를 이용한다.

|
1. 먼저 각 데이타에 번호를 붙인다.
SELECT ROWNUM NO, GUBUN FROM SAM_TAB02;
아래 그림과 같이 나온다.
2. CEIL 함수를 써서 같은 줄(컬)럼에 들어갈 데이타 들에 같은 번호를 부여한다. 4개의 컬럼에 나눠 담을 것이므로 CEIL(ROWNUM/4) 를 사용한다. 이렇게 하면 4개의 데이타 마다 같은 번호를 부여받게 된다( CEIL() 함수는 소수점이 있을경우 올림한 정수를, 소수점 없을 땐 그냥 정수를 그대로 리턴한다)
SELECT CEIL(NO/4) RNO, NO, GUBUN FROM ( SELECT ROWNUM NO, GUBUN FROM SAM_TAB02 )
위를 실행하면 아래와 같이 나온다.
3. 이제 4개씩 묶인 것들에 대해 하나 하나 순차적으로 다시 번호를 할당한다. 이렇게 해야 그게 몇 번째 컬럼으로 쓰일 것인지를 구분할 수 있다. MOD(ROWNUM ,4) 함수를 이용한다. 옆의 MOD 함수는 ROWNUM 을 4로 나눈 나머지들을 리턴한다. 따라서 1,2,3,0 의 값만 나오게 된다.
SELECT CEIL(NO/4) RNO, MOD(NO,4) CNO, NO, GUBUN FROM ( SELECT ROWNUM NO, GUBUN FROM SAM_TAB02 ) 위 쿼리를 실행하면 아래와 같다.
![]()
4. 이제 대각선 모양으로 만들기 위해 DECODE 를 넣어보자.
SELECT CEIL(NO/4) RNO, MOD(NO,4) CNO, NO, DECODE(MOD(NO,4), 1 , GUBUN) C1, DECODE(MOD(NO,4), 2 , GUBUN) C2, DECODE(MOD(NO,4), 3 , GUBUN) C3, DECODE(MOD(NO,4), 0 , GUBUN) C4 --DECODE(MOD(NO,4), 4 , GUBUN) 가 아님에 주의!!! FROM ( SELECT ROWNUM NO, GUBUN FROM SAM_TAB02 )
위의 쿼리를 실행시켜보면 데이터가 아래와 같이 대각선 모양으로 나온다.
5. 이제 MAX 함수를 쓰고 CEIL로 GROUP BY 하여 같은 RNO를 갖는 데이타들을 하나의 ROW 로 만들어 주면 된다. 그룹함수중 자료형에 무관하게 쓸 수 있는 함수로는 MAX 와 MIN 함수가 있는데, 어느 것을 적용해도 NULL 값을 제외하면 C1 부터 C4 까지 각 컬럼 별로 하나의 행만 통과되므로 결과는 동일하다.
SELECT CEIL(NO/4) RNO, --MOD(NO,4) CNO, -- 더이상 필요없는 컬럼 --NO, -- 더이상 필요없는 컬럼 MAX(DECODE(MOD(NO,4), 1 , GUBUN)) C1, MAX(DECODE(MOD(NO,4), 2 , GUBUN)) C2, MAX(DECODE(MOD(NO,4), 3 , GUBUN)) C3, MAX(DECODE(MOD(NO,4), 0 , GUBUN)) C4 FROM ( SELECT ROWNUM NO, GUBUN FROM SAM_TAB02 ) GROUP BY CEIL(NO/4)
위 쿼리를 실행시켜 보면 최초 내가 원했던 모양으로 나오게 된다. 아래처럼 말이다.
|
5.
DANIEL 유저로 로그인 한다
이번에는 두개의 컬럼을 10개의 컬럼으로 변환시키는 방법을 알아본다. 역시 변환되어야 할 컬럼의 갯수가 정해져 있으므로 CEIL 과 MOD 함수를 이용하면 된다.
SELECT *
FROM TEMP

위의 컬럼들중 EMP_ID 와 EMP_NAME 만 뽑아서 한 줄에 다섯 명의 사번과 성명을 보여주는 쿼리를 짜면 된다.
|
1. 먼저 각 데이타에 번호를 붙인다.
SELECT ROWNUM NO, EMP_ID, EMP_NAME FROM TEMP
아래 그림과 같이 나온다.
2. CEIL 함수를 써서 같은 줄(컬럼)들에 들어갈 데이타 들에 같은 번호를 부여한다. 5개의 컬럼에 나눠 담을 것이므로 CEIL(ROWNUM/5) 를 사용한다. 이렇게 하면 5개의 데이타 마다 같은 번호를 부여받게 된다( CEIL() 함수는 소수점이 있을경우 올림한 정수를, 소수점 없을 땐 그냥 정수를 그대로 리턴한다)
SELECT CEIL(NO/5) RNO, NO, EMP_ID, EMP_NAME FROM ( SELECT ROWNUM NO, EMP_ID, EMP_NAME FROM TEMP )
위를 실행하면 아래와 같이 나온다.
3. 이제 5개씩 묶인 것들에 대해 하나 하나 순차적으로 다시 번호를 할당한다. 이렇게 해야 그게 몇 번째 컬럼으로 쓰일 것인지를 구분할 수 있다. MOD(ROWNUM ,5) 함수를 이용한다. 옆의 MOD 함수는 ROWNUM 을 5로 나눈 나머지들을 리턴한다. 따라서 1,2,3,4,0 의 값만 나오게 된다.
SELECT CEIL(NO/5) RNO, MOD(NO,5) CNO, NO, EMP_ID, EMP_NAME FROM ( SELECT ROWNUM NO, EMP_ID, EMP_NAME FROM TEMP )
위 쿼리를 실행하면 아래와 같다.
![]()
4. 이제 대각선 모양으로 만들기 위해 DECODE 를 넣어보자.
SELECT CEIL(NO/5) RNO, MOD(NO,5) CNO, NO, DECODE(MOD(NO,5), 1 , EMP_ID) ID_1, DECODE(MOD(NO,5), 1 , EMP_NAME) NAME_1, DECODE(MOD(NO,5), 2 , EMP_ID) ID_2, DECODE(MOD(NO,5), 2 , EMP_NAME) NAME_2, DECODE(MOD(NO,5), 3 , EMP_ID) ID_3, DECODE(MOD(NO,5), 3 , EMP_NAME) NAME_3, DECODE(MOD(NO,5), 4 , EMP_ID) ID_4, DECODE(MOD(NO,5), 4 , EMP_NAME) NAME_4, DECODE(MOD(NO,5), 0 , EMP_ID) ID_5, DECODE(MOD(NO,5), 0 , EMP_NAME) NAME_5 FROM ( SELECT ROWNUM NO, EMP_ID, EMP_NAME FROM TEMP )
위의 쿼리를 실행시켜보면 데이터가 아래와 같이 대각선 모양으로 나온다.
5. 이제 MAX 함수를 쓰고 CEIL로 GROUP BY 하여 같은 RNO를 갖는 데이타들을 하나의 ROW 로 만들어 주면 된다. 그룹함수중 자료형에 무관하게 쓸 수 있는 함수로는 MAX 와 MIN 함수가 있는데, 어느 것을 적용해도 NULL 값을 제외하면 ID_1 부터 NAME_5 까지 각 컬럼 별로 하나의 행만 통과되므로 결과는 동일하다. 실제 보여지는 컬럼은 ID_1 부터 NAME_5 까지 10개지만 ID_1,NAME_1 컬럼처럼 두 개 컬럼 데이타를 한 번에 가져와서 보여주는 것이므로 CEIL(NO/10) 이 아니라 CEIL(NO/5) 로 해야 한다.
SELECT CEIL(NO/5) RNO, --MOD(NO,5) CNO, --NO, MAX(DECODE(MOD(NO,5), 1 , EMP_ID)) ID_1, MAX(DECODE(MOD(NO,5), 1 , EMP_NAME)) NAME_1, MAX(DECODE(MOD(NO,5), 2 , EMP_ID)) ID_2, MAX(DECODE(MOD(NO,5), 2 , EMP_NAME)) NAME_2, MAX(DECODE(MOD(NO,5), 3 , EMP_ID)) ID_3, MAX(DECODE(MOD(NO,5), 3 , EMP_NAME)) NAME_3, MAX(DECODE(MOD(NO,5), 4 , EMP_ID)) ID_4, MAX(DECODE(MOD(NO,5), 4 , EMP_NAME)) NAME_4, MAX(DECODE(MOD(NO,5), 0 , EMP_ID)) ID_5, MAX(DECODE(MOD(NO,5), 0 , EMP_NAME)) NAME_5 FROM ( SELECT ROWNUM NO, EMP_ID, EMP_NAME FROM TEMP ) GROUP BY CEIL(NO/5) ORDER BY RNO
위 쿼리를 실행시켜 보면 최초 내가 원했던 모양으로 나오게 된다. 아래처럼 말이다.
|
5.
SCOTT/TIGER 유저로 로그인 한다.
emp 테이블에서 부서(deptno)별로 소계를 내려면 아래와 같이 쿼리를 해준다.
SELECT deptno
, ename
, SUM (sal) sal
FROM emp
GROUP BY deptno, ROLLUP (ename)
DEPTNO ENAME SAL
---------- ---------- ----------
10 KING 5000
10 CLARK 2450
10 MILLER 1300
10 8750
20 FORD 3000
20 ADAMS 1100
20 JONES 2975
20 SCOTT 3000
20 SMITH 800
20 10875
30 WARD 1250
30 ALLEN 1600
30 BLAKE 2850
30 JAMES 950
30 MARTIN 1250
30 TURNER 1500
30 9400
그렇다면 부서별로 컬럼별로 나오게 하려면 어떻게 해야하는가?
즉, 아래와 같은 결과를 얻고 싶다면 어떻게 쿼리를 만들어야 하는가?
|
D10 E10 S10 D20 E20 S20 D30 E30 S30 --- ------ ---- --- ----- ----- --- ------ -----10 CLARK 2450 20 ADAMS 1100 30 ALLEN 1600 10 KING 5000 20 FORD 3000 30 BLAKE 2850 10 MILLER 1300 20 JONES 2975 30 JAMES 950 10 8750 20 SCOTT 3000 30 MARTIN 1250 20 SMITH 800 30 TURNER 1500 20 10875 30 WARD 1250 30 9400 |
답변)
아래와 같이 만들어 준다.
사소한 정렬은 조절해 줄 필요가 있겠다.
SELECT MAX (DECODE (deptno, 10, deptno)) d10
, MAX (DECODE (deptno, 10, ename)) e10
, MAX (DECODE (deptno, 10, sal)) s10
, MAX (DECODE (deptno, 20, deptno)) d20
, MAX (DECODE (deptno, 20, ename)) e20
, MAX (DECODE (deptno, 20, sal)) s20
, MAX (DECODE (deptno, 30, deptno)) d30
, MAX (DECODE (deptno, 30, ename)) e30
, MAX (DECODE (deptno, 30, sal)) s30
FROM (SELECT deptno
, ename
, SUM (sal) sal
, ROW_NUMBER () OVER (PARTITION BY deptno ORDER BY 1) rnum
FROM emp
GROUP BY deptno
, ROLLUP (ename))
GROUP BY rnum
| * 다른 방법.
먼저 아래와 같은 형태로 만들고 난 후, decode 를 이용해서 row 들을 column 들로 변환해야 함. 각 deptno 별로 일련 번호 부여해야 함. 그래야 나중에 decode 문 쓴다.
위와 같이 소계가 나오는 데이타를 만들려면 아래의 쿼리를 실행한다. DEPTNO 가 각각 갯수가 다르므로 MOD(ROWNUM/4) 를 해야 할지 MOD(ROWNUM/5)를 해야 할지 알 수 없다. 따라서 ROW_NUMBER() OVER(PARTITION BY DEPTNO ORDER BY DEPTNO) 를 사용해야 한다.
이제 최종적으로 뽑아야 하는 컬럼이 D10 E10 S10 D20 E20 S20 D30 E30 S30 등이므로, SELECT ...DEPTNO_10, ... ENAME_10, ... SALARY_10, ..... FROM ()
의 형식으로 뽑아내기 위해 아래 쿼리를 실행한다. SELECT
위를 실행하면 아래와 같은 모양이 나온다.
RNUM 으로 GROUP BY 해 주면 된다. NULL 값은 함수 연산에 포함되지 않으므로 최종적으로 RNUM 이 같은 것 들중 MAX 함수 처리해주면 된다. 아래와 같다. SELECT 위 쿼리를 실행시키면 최종적인 모습은 아래와 같이 된다.
|
6.
5번과 같이 SCOTT/TIGER 유저로 로그인 한다.
|
위 5번의 결과 RESULTSET 을 다시 보면 아래와 같다.
그런데 5번과 같은 방식으로 DEPT10 과 DEPT20 은 윗 줄에 나오게 하고 DEPT30 만 아래처럼 DEPT10 과 DEPT20 의 아래쪽에 나오게 하려면 어떻게 해야 할까?
쿼리는 동일하다. 다만 DEPT30 부분을 가져오는 DECODE 부분을 모두 주석처리 한 후 UNION ALL 로 상하로 묶어주면 될 것 같다.
아래 쿼리를 실행해 보자.
SELECT MAX(DECODE(DEPTNO,10 , DEPTNO)) DEPTNO_10 , MAX(DECODE(DEPTNO,10 , ENAME)) ENAME_10, MAX(DECODE(DEPTNO,10 , SUM_SAL)) SAL_10, MAX(DECODE(DEPTNO,20 , DEPTNO)) DEPTNO_20, MAX(DECODE(DEPTNO,20 , ENAME)) ENAME_20, MAX(DECODE(DEPTNO,20 , SUM_SAL)) SAL_20, --MAX(DECODE(DEPTNO,30 , DEPTNO)) DEPTNO_30, --MAX(DECODE(DEPTNO,30 , ENAME)) ENAME_30, --MAX(DECODE(DEPTNO,30 , SUM_SAL)) SAL_30, RNUM FROM ( SELECT DEPTNO, ENAME, --REF, SUM_SAL, RNUM FROM ( SELECT DEPTNO, ENAME, SUM(SAL) SUM_SAL, ROW_NUMBER() OVER(PARTITION BY DEPTNO ORDER BY DEPTNO) RNUM, GROUPING_ID(DEPTNO, ENAME) REF FROM EMP GROUP BY ROLLUP(DEPTNO, ENAME) ) WHERE REF <> 3 -- 소계말고 총계 나오는 부분. ) GROUP BY RNUM UNION ALL SELECT --MAX(DECODE(DEPTNO,10 , DEPTNO)) DEPTNO_10 , --MAX(DECODE(DEPTNO,10 , ENAME)) ENAME_10, --MAX(DECODE(DEPTNO,10 , SUM_SAL)) SAL_10, --MAX(DECODE(DEPTNO,20 , DEPTNO)) DEPTNO_20, --MAX(DECODE(DEPTNO,20 , ENAME)) ENAME_20, --MAX(DECODE(DEPTNO,20 , SUM_SAL)) SAL_20, MAX(DECODE(DEPTNO,30 , DEPTNO)) DEPTNO_30, MAX(DECODE(DEPTNO,30 , ENAME)) ENAME_30, MAX(DECODE(DEPTNO,30 , SUM_SAL)) SAL_30, NULL , NULL, NULL, RNUM FROM ( SELECT DEPTNO, ENAME, --REF, SUM_SAL, RNUM FROM ( SELECT DEPTNO, ENAME, SUM(SAL) SUM_SAL, ROW_NUMBER() OVER(PARTITION BY DEPTNO ORDER BY DEPTNO) RNUM, GROUPING_ID(DEPTNO, ENAME) REF FROM EMP GROUP BY ROLLUP(DEPTNO, ENAME) ) WHERE REF <> 3 -- 소계말고 총계 나오는 부분. ) GROUP BY RNUM ORDER BY RNUM
를 실행하면 아래와 같다.
원하던 바대로 나오지를 않는다. ORDER BY 쪽의 문제로 DEPT10 과 DEPT30 이 교차되어 나온다. 따라서 DEPT10 이 먼저 나오도록 아래처럼 LEVEL 이라는 필드를 하나 더 만들어 준다.
SELECT 1 LEV, MAX(DECODE(DEPTNO,10 , DEPTNO)) DEPTNO_10 , MAX(DECODE(DEPTNO,10 , ENAME)) ENAME_10, MAX(DECODE(DEPTNO,10 , SUM_SAL)) SAL_10, MAX(DECODE(DEPTNO,20 , DEPTNO)) DEPTNO_20, MAX(DECODE(DEPTNO,20 , ENAME)) ENAME_20, MAX(DECODE(DEPTNO,20 , SUM_SAL)) SAL_20, --MAX(DECODE(DEPTNO,30 , DEPTNO)) DEPTNO_30, --MAX(DECODE(DEPTNO,30 , ENAME)) ENAME_30, --MAX(DECODE(DEPTNO,30 , SUM_SAL)) SAL_30, RNUM FROM ( SELECT DEPTNO, ENAME, --REF, SUM_SAL, RNUM FROM ( SELECT DEPTNO, ENAME, SUM(SAL) SUM_SAL, ROW_NUMBER() OVER(PARTITION BY DEPTNO ORDER BY DEPTNO) RNUM, GROUPING_ID(DEPTNO, ENAME) REF FROM EMP GROUP BY ROLLUP(DEPTNO, ENAME) ) WHERE REF <> 3 -- 소계말고 총계 나오는 부분. ) GROUP BY RNUM UNION ALL SELECT 2 LEV, --MAX(DECODE(DEPTNO,10 , DEPTNO)) DEPTNO_10 , --MAX(DECODE(DEPTNO,10 , ENAME)) ENAME_10, --MAX(DECODE(DEPTNO,10 , SUM_SAL)) SAL_10, --MAX(DECODE(DEPTNO,20 , DEPTNO)) DEPTNO_20, --MAX(DECODE(DEPTNO,20 , ENAME)) ENAME_20, --MAX(DECODE(DEPTNO,20 , SUM_SAL)) SAL_20, MAX(DECODE(DEPTNO,30 , DEPTNO)) DEPTNO_30, MAX(DECODE(DEPTNO,30 , ENAME)) ENAME_30, MAX(DECODE(DEPTNO,30 , SUM_SAL)) SAL_30, NULL , NULL, NULL, RNUM FROM ( SELECT DEPTNO, ENAME, --REF, SUM_SAL, RNUM FROM ( SELECT DEPTNO, ENAME, SUM(SAL) SUM_SAL, ROW_NUMBER() OVER(PARTITION BY DEPTNO ORDER BY DEPTNO) RNUM, GROUPING_ID(DEPTNO, ENAME) REF FROM EMP GROUP BY ROLLUP(DEPTNO, ENAME) ) WHERE REF <> 3 -- 소계말고 총계 나오는 부분. ) GROUP BY RNUM ORDER BY LEV, RNUM 위의 쿼리를 실행시켜보면 아래와 같이 최초 의도한대로 잘 나온다.
![]()
|
7.
UNION ALL 을 이용해야 하는 경우도 있다.
GROUP BY GROUPING SETS 로 구한 RESULTSET 을 가로로 정렬해 본다.
SCOTT/TIGER 로 로그인한다.
|
GROUP BY GROUPING SETS 로 구한 RESULTSET 을 가로로 정렬해 본다.
SELECT DEPTNO, ENAME, JOB, SUM(SAL) SS FROM EMP T GROUP BY GROUPING SETS ( DEPTNO, ENAME, JOB )
위의 쿼리를 실행하면 아래 그림과 같이 ENAME 별로 급여 SUM 한 것, JOB 별로 급여를 SUM 한 것, DEPTNO 별로 급여를 SUM 한 것들이 각각 묶음으로 서로 다른 필드와 ROW 에 나오게 된다.
최종적으로 아래와 같은 모습이 되어야 한다.
(원래 테이블이 자주 사용되니까 WITH 구문을 사용한다)
WITH TT AS ( SELECT DEPTNO, ENAME, JOB, SUM(SAL) SS FROM EMP T GROUP BY GROUPING SETS ( DEPTNO, ENAME, JOB ) ) SELECT ENAME, SS, GRP, RNUM FROM ( SELECT TO_CHAR(ENAME) ENAME, DECODE(ENAME,NULL, NULL ,SS) SS, DECODE(ENAME,NULL, NULL ,1 ) GRP, DECODE(ENAME,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY ENAME)) RNUM FROM TT UNION ALL SELECT TO_CHAR(JOB), DECODE(JOB,NULL, NULL ,SS), DECODE(JOB,NULL, NULL ,2) , DECODE(JOB,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY JOB)) FROM TT UNION ALL SELECT TO_CHAR(DEPTNO) , DECODE(DEPTNO,NULL, NULL ,SS), DECODE(DEPTNO,NULL, NULL ,3) , DECODE(DEPTNO,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY DEPTNO)) FROM TT ) WHERE GRP IS NOT NULL
위의 쿼리를 실행하면 아래와 같은 모습이 나온다. 맨 윗 부분은 캡처에서 잘렸음.
GRP 필드가 각 그룹별(ENAME,JOB,DEPTNO 별) 번호이고 RNUM 이 각 그룹 안에서 또 다시 일련번호를 부여한 것이다. 이제 컬럼들이
ENAME, SUM_ENAME, JOB, SUM_JOB, DEPTNO, SUM_DEPTNO 의 순서로 나오도록 세로에 쫙 나와 있는 데이타들을 가로로(새로운 컬럼으로) 옮겨보자.
WITH TT AS
(
SELECT
DEPTNO,
ENAME,
JOB,
SUM(SAL) SS
FROM EMP T
GROUP BY GROUPING SETS
(
DEPTNO,
ENAME,
JOB
)
)
SELECT
DECODE(GRP,1,ENAME) ENAME,
DECODE(GRP,1,SS) SUM_ENAME,
DECODE(GRP,2,ENAME) JOB,
DECODE(GRP,2,SS) SUM_JOB,
DECODE(GRP,3,ENAME) DEPTNO,
DECODE(GRP,3,SS) SUM_DEPTNO,
GRP,
RNUM
FROM
(
SELECT TO_CHAR(ENAME) ENAME,
DECODE(ENAME,NULL, NULL ,SS) SS,
DECODE(ENAME,NULL, NULL ,1 ) GRP,
DECODE(ENAME,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY ENAME)) RNUM
FROM TT
UNION ALL
SELECT TO_CHAR(JOB),
DECODE(JOB,NULL, NULL ,SS),
DECODE(JOB,NULL, NULL ,2) ,
DECODE(JOB,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY JOB))
FROM TT
UNION ALL
SELECT TO_CHAR(DEPTNO) ,
DECODE(DEPTNO,NULL, NULL ,SS),
DECODE(DEPTNO,NULL, NULL ,3) ,
DECODE(DEPTNO,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY DEPTNO))
FROM TT
)
WHERE GRP IS NOT NULL
위의 쿼리를 실행하면 아래와 같이 된다. 맨 윗 부분은 캡처되지 않았음.
![]() 최초의 모습과 달라진 점은 각 ENAME, JOB, DEPTNO 필드 바로 옆에 SUM(..) 된 필드가 생겼다는 것이다.
이제 JOB 부분과 DEPTNO 부분을 ENAME 처럼 맨 위로 올리면 된다.
RNUM 으로 GROUP BY 하면 된다. 다만 GROUP BY 하고도 이상하게 정렬이 되지 않는다.
ORDER BY RNUM 해도 정렬이 되지 않아서 ORDER BY TO_NUMBER(RNUM) ASC 를 붙여야 한다.
최종적으로 아래의 쿼리를 작성한다.
WITH TT AS
(
SELECT
DEPTNO,
ENAME,
JOB,
SUM(SAL) SS
FROM EMP T
GROUP BY GROUPING SETS
(
DEPTNO,
ENAME,
JOB
)
)
SELECT MIN(DECODE(GRP,1,ENAME)) ENAME,--MAX 함수여도 아무 관계없다.
MIN(DECODE(GRP,1,SS)) SUM_ENAME,--MAX 함수여도 아무 관계없다.
MIN(DECODE(GRP,2,ENAME)) JOB,
MIN(DECODE(GRP,2,SS)) SUM_JOB,
MIN(DECODE(GRP,3,ENAME)) DEPTNO,
MIN(DECODE(GRP,3,SS)) SUM_DEPTNO,
--GRP,
RNUM
FROM
(
SELECT TO_CHAR(ENAME) ENAME,
DECODE(ENAME,NULL, NULL ,SS) SS,
DECODE(ENAME,NULL, NULL ,1 ) GRP,
DECODE(ENAME,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY ENAME)) RNUM
FROM TT
UNION ALL
SELECT TO_CHAR(JOB),
DECODE(JOB,NULL, NULL ,SS),
DECODE(JOB,NULL, NULL ,2) ,
DECODE(JOB,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY JOB))
FROM TT
UNION ALL
SELECT TO_CHAR(DEPTNO) ,
DECODE(DEPTNO,NULL, NULL ,SS),
DECODE(DEPTNO,NULL, NULL ,3) ,
DECODE(DEPTNO,NULL, NULL ,ROW_NUMBER() OVER(ORDER BY DEPTNO))
FROM TT
)
WHERE GRP IS NOT NULL
GROUP BY RNUM
ORDER BY TO_NUMBER(RNUM) ASC
위의 쿼리를 실행해 보면, 처음 의도한 대로 잘 나오는걸 확인할 수 있다.
![]()
|
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
댓글 0
| 번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
|---|---|---|---|---|
| 공지 | 오라클 기본 샘플 데이터베이스 | 졸리운_곰 | 2014.01.02 | 86289 |
| 공지 | [SQL컨셉] 서적 "SQL컨셉"의 샘플 데이타 베이스 SAMPLE DATABASE of ORACLE | 가을의 곰을... | 2013.02.10 | 78748 |
| 공지 | [G_SQL] Sample Database | 가을의 곰을... | 2012.05.20 | 95490 |
| 5 | XML database | 졸리운_곰 | 2016.11.29 | 4392 |
| 4 |
Learn XQuery in 10 Minutes: An XQuery Tutorial
| 졸리운_곰 | 2016.11.29 | 1997 |
| 3 |
xquery-tutorial.pdf
| 졸리운_곰 | 2016.11.29 | 1727 |
| 2 | XML 문서 가져오기 및 XQuery 쿼리 예제 | 졸리운_곰 | 2016.11.29 | 1563 |
| 1 |
[SQL] XQuery로 XML Data에 접근하기
| 졸리운_곰 | 2016.11.29 | 1921 |


































