GROUP BY

1
2
3
4
5
6
SELECT
    DEPTNO
    ,JOB
    ,SUM(SAL)
FROM EMP
GROUP BY DEPTNO, JOB;

ORACLE SCOTT 계정의 EMP 테이블을 사용해서  실습을 진행했습니다.

일반적으로 사용하는 GROUP BY 문입니다. SUM 함수를 이용해서 각 부서별, 직업별 급여의 합을 구했습니다.

 

ROLLUP

1
2
3
4
5
6
SELECT
    DEPTNO
    ,JOB
    ,SUM(SAL)
FROM EMP
GROUP BY ROLLUP(DEPTNO, JOB);

위의 GROUP BY절에 ROLLUP을 추가했습니다. 결과를  부서별, 직업별 뿐만아니라 전체 급여의 합과 부서별 급여의 합을 함께 출력합니다. 그런데 여기서 의문이 드는게 있습니다. 전체 급여로도 합을 계산해주고 부서별로도 합을 구해주는건 좋은데... 전체 급여의 합만 보여주던가, 부서별로 합만 보여주던가 둘 중 하나만 추가하고 싶은데... 그게 가능할까요?

 

전체 급여 합만 출력

1
2
3
4
5
6
SELECT
    DEPTNO
    ,JOB
    ,SUM(SAL)
FROM EMP
GROUP BY ROLLUP((DEPTNO, JOB));

가능하네요.

 

부서별 급여 합만 출력

1
2
3
4
5
6
SELECT
    DEPTNO
    ,JOB
    ,SUM(SAL)
FROM EMP
GROUP BY DEPTNO, ROLLUP(JOB);

부서별 급여 합만 출력하는 것도 가능하네요. 그럼 도데체 ROLLUP이 어떻게 무슨 원리로 이렇게 가능한걸까요?

 

 

ROLLUP의 원리

위 이미지가 ROLLUP의 원리를 전부 설명해줍니다. 각 번호는 해당 칼럼으로 GROUP BY를 한 결과를 준다는 뜻입니다. 첫번째 GROUP BY절 같은 경우는 총 4개의 GROUP을 만든다는 뜻 입니다. ROLLUP은 아래와 같은 원리로 실행됩니다.

1. ROLLUP의 인자로 들어온 칼럼을 오른쪽부터 하나씩 빼면서 GROUP을 만듭니다.
2. "()"의 의미는 GROUP이 없는 즉, 전체에 대한 결과를 출력한다는 뜻 입니다. EX(SUM 함수 사용하면 전체 SUM 구한다는 뜻)
3. 괄호로 묶여져 있는 컬럼은 하나로 본다는 뜻 입니다.
4. ROLLUP 이전에 일반 컬럼과 GROUP BY 한다면, 일반 컬럼은 끝까지 남습니다.

 

 

GROUP BY ROLLUP(A, B, C) 예시

1
2
3
4
5
6
7
SELECT
    DEPTNO
    ,JOB
    ,ENAME
    ,SUM(SAL)
FROM EMP
GROUP BY ROLLUP(DEPTNO,JOB,ENAME);

 

 

 

GROUP BY ROLLUP(A, (B, C)) 예시

1
2
3
4
5
6
7
SELECT
    DEPTNO
    ,JOB
    ,ENAME
    ,SUM(SAL)
FROM EMP
GROUP BY ROLLUP(DEPTNO,(JOB,ENAME));

 

 

 

GROUP BY A, ROLLUP((B, C)) 예시

1
2
3
4
5
6
7
SELECT
    DEPTNO
    ,JOB
    ,ENAME
    ,SUM(SAL)
FROM EMP
GROUP BY DEPTNO,ROLLUP((JOB,ENAME));

 

 

 

ROLLUP 특이 예제

여태까지는 기본적으로 ROLLUP이 어떤 원리로 실행되는지 알아봤습니다. 그럼 위에 예제 처럼 결과를 출력할 수 있을까요? 일단, 결과를 보니, 한 칼럼에서 컬럼 이외의 값이 나오기도하고, 급여 컬럼에서는 평균값과 합계값이 같이 나오는 것을 볼 수 있습니다. 어떻게 결과를 이렇게 출력할 수 있을까요? 우선 GROUPING, GROUPING_ID 그리고 숫자 GROUP 추가에 대한 내용을 알아야합니다.

 

 

GROUPING

1
2
3
4
5
6
7
8
SELECT
    DEPTNO
    ,GROUPING(DEPTNO) AS DG
    ,JOB
    ,GROUPING(JOB) AS JG
    ,SUM(SAL)
FROM EMP
GROUP BY ROLLUP(DEPTNO, JOB);

GROUPING 함수는 ROLLUP이랑 같이 사용합니다. 해당 칼럼이 ROLLUP 되었을 때 그룹에서 빠져있다면 1을 반환합니다. 즉, GROUP BY ROLLUP 결과로 NULL이 나왔을 때 1을 반환합나다.

 

 

GROUPING_ID

1
2
3
4
5
6
7
8
9
SELECT
    DEPTNO
    ,JOB
    ,GROUPING(DEPTNO) AS DG
    ,GROUPING(JOB) AS JG
    ,GROUPING_ID(DEPTNO, JOB) GI
    ,SUM(SAL)
FROM EMP
GROUP BY ROLLUP(DEPTNO, JOB);

GROUPING_ID 함수는 인자로 들어온 각각 칼럼의 GROUPING 함수 값을 2진수로 합쳐 해당 2진수 값을 10진수로 변환한 값을 반환해줍니다.

 

 

숫자 GROUP

1
2
3
4
5
6
ELECT
    DEPTNO
    ,JOB
    ,SUM(SAL)
FROM EMP
GROUP BY ROLLUP(1,DEPTNO, JOB);

숫자가 들어간 ROLLUP은 뭘까요? 결과를 보니 마지막 2개의 줄이 같은 값이 나왔습니다. 헷갈릴 수 있지만 ROLLUP의 원리를 생각해보면 별 다를것 없습니다. JOB과 DEPTNO가 ROLLUP에서 빠지고 1만 남았을때 SUM(SAL)을 구하려고 합니다. 상수에 대한 GROUP은 없습니다. 즉, 전체 행에 대한 결과를 출력하라는 의미와 같습니다.

 

 

GROUPING, GROUPING_ID, 숫자 GROUP 종합

1
2
3
4
5
6
7
8
9
10
11
SELECT
    DEPTNO
    ,JOB
    ,GROUPING(1) AS "1"
    ,GROUPING(DEPTNO) AS "D"
    ,GROUPING(2) AS "2"
    ,GROUPING(JOB) AS "J"
    ,GROUPING_ID(1, DEPTNO, 2, JOB) AS ID
    ,SUM(SAL)
FROM EMP
GROUP BY ROLLUP(1,DEPTNO, 2, JOB);

GROUPING, GROUPING_ID, 숫자 GROUP 모두 같이 사용했습니다. 한 테이블에 놓고 보니까 이해가 가시나요? 숫자가 ROLLUP에 온다고 해도 ROLLUP의 원리를 안다면 큰 차이가 없습니다. 그냥 남아있는 칼럼들로 GROUP BY한 결과를 출력해주고 원리대로 진행합니다.

 

 

최종 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
SELECT
    DECODE(
        GROUPING_ID(1, DEPTNO, 2, JOB)
        ,7'전체'
        ,15'전체'
        ,DEPTNO
    ) AS DEPTNO
    ,DECODE(
        GROUPING_ID(1, DEPTNO, 2, JOB)
        ,1'합계'
        ,3'평균'
        ,7'합계'
        ,15'평균'
        ,JOB
    ) AS JOB
    ,DECODE(
        GROUPING_ID(1, DEPTNO, 2, JOB)
        ,3, ROUND(AVG(SAL))
        ,15, ROUND(AVG(SAL))
        ,SUM( SAL)
    ) AS SAL
FROM EMP
GROUP BY ROLLUP(1,DEPTNO, 2, JOB);

 

출처 : https://myjamong.tistory.com/191

테이블 정보 상세보기
F4 : Table, View, Proc, Funct, Package를 DESC(테이블명 위에 커서를 두고 F4키)

그리드데이터 복사
Ctrl+C : 데이터만 복사됨
Ctrl+Insert : 헤더 + 데이터 복사됨

자동완성
Ctrl+. : Table Completion (매칭되는 테이블목록 출력)
Ctrl+T : Columns Dropdown (해당테이블의 컬럼목록 표시)

SQL문 실행
F5 : SQL Editor내의 모든 SQL문 실행
Ctrl+Enter : 현재 커서의 SQL문 실행
F9 : SQL문 실행 후 Grid에 출력

히스토리(과거 수행SQL문 조회)
F8 : 과거에 실행한SQL문 HISTORY 목록
Alt+Up : History UP
Alt+Down : History DOWN

텍스트 대/소문자 변환
CTRL+L : 텍스트를 소문자로
CTRL+U : 텍스트를 대문자로

주석처리
Ctrl+B : 주석처리
Ctrl+Shift+B : 주석해제

편집 창 전환(이동)
F6 : SQL Editor와 결과창간의 이동
F2 : SQL Editor창 전체화면 전환
Shift+F2 : Grid Output창 전체화면 전환

기타 단축키
F7 : 화면을 모두 CLEAR
Ctrl+Shift+F : 쿼리문을 보기좋게 정렬
Ctrl+F9 : SQL문 Validate (SQL문을 수행하지 않음)


F1 : Toad 도움말 파일의 SQL Editor 부분이 표시됩니다.
F2 : 전체 화면 Editor와 Editor/Results 패널 표시 장치 사이를 전환합니다.
<SHIFT>F2 : 전체 화면 그리드를 전환합니다.
F3 : 다음으로 일치하는 것을 찾습니다.
<SHIFT>F3 : 이전에 일치하는 것을 찾습니다.
F4 : 팝업 창의 테이블, 뷰, 프로시저, 함수, 또는 패키지를 설명합니다.
F5 : 스크립트로 실행합니다.
F6 : 커서를 Editor와 Results 패널 사이로 전환합니다.
F7 : 모든 텍스트를 지웁니다.
F8 : 이전 SQL 문을 재호출합니다(SQL Statement Recall 창을 불러옵니다).
F9 : 실행문을 실행합니다.
<CTRL>F9 : 실행(구문 분석) 없이 실행문을 검사합니다.
<SHIFT>F9 : 커서 위치에서 현재 실행문을 실행합니다.
F10 : 오른쪽 클릭 메뉴를 표시합니다.
F11 : Script와 같은 것 실행(=F5)
F12 : 편집기 내용을 지정된 외부 편집기로 전달합니다.
<CTRL>A : 모든 텍스트를 선택합니다.
<CTRL>C : 복사
<CTRL>D : 프로시저 인수를 표시합니다.
<CTRL>E : 현재 실행문에서 Explain Plan을 실행합니다.
<CTRL>F : 텍스트를 찾습니다(Find Text 창을 불러옵니다).
<CTRL>G : 라인으로 이동합니다(Goto Line 창을 불러옵니다).
<CTRL>L : 텍스트를 소문자로 변환합니다.
<CTRL>M : Make Code Statement
<CTRL>N : 이름이 지정된 SQL 문을 재호출합니다(SQL Statement Recall 창을 불러옵니다).
<CTRL>O : 텍스트 파일을 엽니다.
<CTRL>P : Strip Code Statement(쓸데없는 태그들을 정리해 줍니다. 유용함)
<CTRL>R : 검색 및 바꾸기(Find and Replace Text 창을 불러옵니다)
<CTRL>S : 파일을 저장합니다.
<SHIFT><CTRL>S : 파일을 다른 이름으로 저장합니다.
<CTRL>T : 열 드롭다운을 표시합니다.
<CTRL>U : 텍스트를 대문자로 변환합니다.
<CTRL>V : 붙여넣기
<CTRL>X : 잘라내기
<SHIFT><CTRL>Z : 마지막으로 취소한 작업을 재실행합니다.
<ALT><UP> : 이전 실행문을 표시합니다.
<ALT><DOWN> : 다음 실행문을 표시합니다(<ALT><UP>을 사용한 후 사용)
<ALT><PgUp> : 이전 탭으로 이동
<ALT><PgDn> : 다음 탭으로 이동
<CTRL><ALT><PgUp> : 이전 결과 패널 탭으로 이동
<CTRL><ALT><PgDn> : 다음 결과 패널 탭으로 이동
<CTRL><HOME> : 데이터 그리드에서는 맨 위의 레코드셋으로 이동하며, 결과 그리드에서는 커서가 위치한행의 첫 번째 열로 이동하고, 편집기에서는 텍스트의 첫 번째 열과 첫 번째 행으로 이동합니다.
<CTRL><END> : 데이터 그리드에서는 레코드셋의 맨 끝으로 이동하며, 편집기에서는 텍스트의 마지막 열과 마지막 행으로 이동합니다. 이 단원의 "주의"를 참조하십시오.
<CTRL><SPACE> : 코드 완성 템플릿을 활성화합니다.
<CTRL><TAB> : MDI Child 창의 콜렉션을 순환합니다.
<CTRL><ENTER> : 커서 이치에서 현재 SQL 문을 실행합니다.
<CTRL>. (마침표) : 테이블 이름을 자동으로 완성합니다.

CREATE TABLE T_PLAN_2 (
PLAN_DATE   VARCHAR2(8 BYTE), 
MAT_CODE   VARCHAR2(10 BYTE), 
PLAN_QTY   NUMBER
);
INSERT INTO T_PLAN_2 VALUES ('20200701','A01',100);
INSERT INTO T_PLAN_2 VALUES ('20200702','A01',100);
INSERT INTO T_PLAN_2 VALUES ('20200703','A01',100);
INSERT INTO T_PLAN_2 VALUES ('20200701','A02',90);
INSERT INTO T_PLAN_2 VALUES ('20200703','A02',90);
INSERT INTO T_PLAN_2 VALUES ('20200701','B01',150);
INSERT INTO T_PLAN_2 VALUES ('20200702','B01',150);
INSERT INTO T_PLAN_2 VALUES ('20200703','B01',150);
INSERT INTO T_PLAN_2 VALUES ('20200801','A01',100);
INSERT INTO T_PLAN_2 VALUES ('20200802','A03',100);
INSERT INTO T_PLAN_2 VALUES ('20200803','A01',100);
INSERT INTO T_PLAN_2 VALUES ('20200801','A02',90);
INSERT INTO T_PLAN_2 VALUES ('20200802','A02',90);
INSERT INTO T_PLAN_2 VALUES ('20200803','A02',90);
INSERT INTO T_PLAN_2 VALUES ('20200802','B01',150);
INSERT INTO T_PLAN_2 VALUES ('20200803','B01',150);
INSERT INTO T_PLAN_2 VALUES ('20200901','A03',90);
INSERT INTO T_PLAN_2 VALUES ('20200902','A01',95);
INSERT INTO T_PLAN_2 VALUES ('20200903','A04',85);
INSERT INTO T_PLAN_2 VALUES ('20200901','A02',100);
INSERT INTO T_PLAN_2 VALUES ('20200902','A02',90);
INSERT INTO T_PLAN_2 VALUES ('20200903','A04',70);
INSERT INTO T_PLAN_2 VALUES ('20200902','B01',130);
INSERT INTO T_PLAN_2 VALUES ('20200903','B02',120);
INSERT INTO T_PLAN_2 VALUES ('20201001','A02',95);
INSERT INTO T_PLAN_2 VALUES ('20201002','A01',70);
INSERT INTO T_PLAN_2 VALUES ('20201003','A01',110);
INSERT INTO T_PLAN_2 VALUES ('20201001','A02',90);
INSERT INTO T_PLAN_2 VALUES ('20201002','B01',85);
INSERT INTO T_PLAN_2 VALUES ('20201003','A04',90);
INSERT INTO T_PLAN_2 VALUES ('20201002','B02',80);
INSERT INTO T_PLAN_2 VALUES ('20201003','B02',90);

COMMIT;

-- 샘플 데이터 생성 스크립트

CREATE TABLE T_PROD (
PROD_DATE   VARCHAR2(8 BYTE), 
MAT_CODE   VARCHAR2(10 BYTE), 
PROD_QTY   NUMBER
);
INSERT INTO T_PROD VALUES ('20200701','A01',101);
INSERT INTO T_PROD VALUES ('20200702','A01',112);
INSERT INTO T_PROD VALUES ('20200703','A01',84);
INSERT INTO T_PROD VALUES ('20200701','A02',80);
INSERT INTO T_PROD VALUES ('20200702','A02',93);
INSERT INTO T_PROD VALUES ('20200703','A02',110);
INSERT INTO T_PROD VALUES ('20200701','B01',154);
INSERT INTO T_PROD VALUES ('20200702','B01',148);
INSERT INTO T_PROD VALUES ('20200703','B01',139);
INSERT INTO T_PROD VALUES ('20200801','A01',102);
INSERT INTO T_PROD VALUES ('20200802','A01',99);

INSERT INTO T_PROD VALUES ('20200801','A02',79);
INSERT INTO T_PROD VALUES ('20200802','A02',48);
INSERT INTO T_PROD VALUES ('20200803','A02',49);
INSERT INTO T_PROD VALUES ('20200801','B01',143);
INSERT INTO T_PROD VALUES ('20200802','B01',162);
INSERT INTO T_PROD VALUES ('20200803','B01',155);

CREATE TABLE T_PLAN (
PLAN_DATE   VARCHAR2(8 BYTE), 
MAT_CODE   VARCHAR2(10 BYTE), 
PLAN_QTY   NUMBER
);
INSERT INTO T_PLAN VALUES ('20200701','A01',100);
INSERT INTO T_PLAN VALUES ('20200702','A01',100);
INSERT INTO T_PLAN VALUES ('20200703','A01',100);
INSERT INTO T_PLAN VALUES ('20200701','A02',90);

INSERT INTO T_PLAN VALUES ('20200703','A02',90);
INSERT INTO T_PLAN VALUES ('20200701','B01',150);
INSERT INTO T_PLAN VALUES ('20200702','B01',150);
INSERT INTO T_PLAN VALUES ('20200703','B01',150);
INSERT INTO T_PLAN VALUES ('20200801','A01',100);
INSERT INTO T_PLAN VALUES ('20200802','A01',100);
INSERT INTO T_PLAN VALUES ('20200803','A01',100);
INSERT INTO T_PLAN VALUES ('20200801','A02',90);
INSERT INTO T_PLAN VALUES ('20200802','A02',90);
INSERT INTO T_PLAN VALUES ('20200803','A02',90);

INSERT INTO T_PLAN VALUES ('20200802','B01',150);
INSERT INTO T_PLAN VALUES ('20200803','B01',150);

CREATE TABLE T_MATERIAL (
MAT_CODE   VARCHAR2(10 BYTE), 
MAT_DESC   VARCHAR2(100 BYTE)
);
INSERT INTO T_MATERIAL VALUES ('A01','FAN');
INSERT INTO T_MATERIAL VALUES ('A02','REFRIGERATOR');
INSERT INTO T_MATERIAL VALUES ('A03','WASHER');
INSERT INTO T_MATERIAL VALUES ('A04','VACUUM');
INSERT INTO T_MATERIAL VALUES ('B01','DRYER');
INSERT INTO T_MATERIAL VALUES ('B02','FRIDGE');

COMMIT;

-- 샘플 데이터 생성 스크립트
CREATE TABLE T_JOIN_A (
COL_A   VARCHAR2(10 BYTE), 
COL_B   VARCHAR2(10 BYTE)
);
INSERT INTO T_JOIN_A VALUES ('A001','B001');
INSERT INTO T_JOIN_A VALUES ('A002','B001');
INSERT INTO T_JOIN_A VALUES ('A003','B002');
INSERT INTO T_JOIN_A VALUES ('A004','B003');
INSERT INTO T_JOIN_A VALUES ('A005','B008');

CREATE TABLE T_JOIN_B (
COL_B   VARCHAR2(10 BYTE), 
COL_C   VARCHAR2(10 BYTE)
);
INSERT INTO T_JOIN_B VALUES ('B001','C001');
INSERT INTO T_JOIN_B VALUES ('B002','C002');
INSERT INTO T_JOIN_B VALUES ('B003','C003');
INSERT INTO T_JOIN_B VALUES ('B004','C004');
INSERT INTO T_JOIN_B VALUES ('B005','C005');
INSERT INTO T_JOIN_B VALUES ('B006','C006');
INSERT INTO T_JOIN_B VALUES ('B007','C007');

COMMIT;

+ Recent posts