본문 바로가기
자격증/SQLD(SQL 개발자)

[SQL활용] 그룹 함수

by 어쩌다개발 2017. 12. 6.
반응형

1. 데이터 분석 개요

 

1) ANSI/ISO SQL 표준에서 정의하는 데이터 분석 함수

1-1)AGGREGATE FUNCTION

-GROUP AGGREGATE FUNCTION이라고도 부르며, GROUP FUNCTION의 한 부분으로 분류할 수 있음.

-COUNT, SUM, AVG, MAX, MIN 외 각종 집계 함수들이 포함되어 있음. 

 

2) GROUP FUNCTION

2-1) 소계, 중계, 합계, 총 합계 등 여러 레벨의 결산 보고서를 만들거나 통계 데이터를 뽑을 때 레베별 집계를 위한 여러 단계의 SQL을 UNION, UNION ALL로 묶은 후 하나의 테이블을 여러번 다시 읽어 다시 재 정렬하는 복잡한 단계를 거쳤음.

2-2) 그룹 함수를 사용하면 하나의 SQL로 테이블을 한 번만 읽어서 빠르게 원하는 데이터를 뽑을 수 있음.

2-3) 소계/합계를 표시하기 위해 GROUPING 함수와 CASE 함수를 이용하면 쉽게 원하는 포맷의 데이터를 뽑을 수 있음.

2-4) 그룹 함수로는 집계 함수를 제외하고

2-4-1) ROULLUP 함수 : 

- 소그룹 간의 소계를 계산

- GROUP BY의 확장된 형태로 사용하기가 쉬우며 병렬로 수행이 가능, 시간 및 지역처럼 계층적 분류를 포함하고 있는 데이터의 집계에 적합

2-4-2) CUBE 함수 : 

- .GROUP BY 항목들 간 다차원적인 소계를 계산

- 결합 가능한 모든 값에 대하여 다차원적인 집계를 생성하게 되므로 ROJLLUP에 비해 다양한 데이터를 얻는 장점

- 시스템에 부하를 많이 주는 단점

2-4-3) GROUPING SETS 함수 : 

특정 항목에 대한 소계를 계산

- 원하는 부분의 소계만 손쉽게 추출

 

3) WINDOW FUNCTION

분석 함수나 순위 함수로 알려져 있으며 데이터웨어하우스에서 발전한 기능

2. ROLLUP 함수

 

1) Grouping Coumns의 List 

- SubTotal을 생성하기 위해 사용

- Grouping Columns의 수를 n이라고 하면 n+1 Level의 SubTotal이 생성

- ROLLUP의 인수는 계층 구조이므로 인수 순서가 바뀌면 수행 결과도 바뀌게 되므로 인수의 순서에 주의

 

STEP1. 일반적인 GROUP BY 절 사용

 

SELECT

DNAME

,JOB

,COUNT(*) AS TOTAL_EMPL

,SUM(SAL) AS TOTAL_SAL

FROM EMP E, DEPT DP

WHERE E.DEPTNO = DP.DEPTNO

GROUP BY DNAME, JOB

▲ Oracle을 포함한 일부 DBMS의 과거 버전에서는 GROUP BY 절 사용시 자동적으로 정렬을 수행 > 현재 대부분의 DBMS 버전은 집계 기능만 지원하고 있으며 정렬이 필요한 경우 ORDER BY 절에 명시적으로 정렬 컬럼이 표시되어야 함.

결과)

 

STEP1-1. GROUP BY 절 + ORDER BY 절 사용

 

SELECT

DNAME

,JOB

,COUNT(*) AS TOTAL_EMPL

,SUM(SAL) AS TOTAL_SAL

FROM EMP E, DEPT DP

WHERE E.DEPTNO = DP.DEPTNO

GROUP BY DNAME, JOB

ORDER BY DNAME, JOB

STEP2. ROLLUP 함수 사용

 

--ORCLE

SELECT

DNAME

,JOB

,COUNT(*) AS TOTAL_EMPL

,SUM(SAL) AS TOTLA_SAL

FROM EMP E, DEPT DP

WHERE E.DEPTNO = DP.DEPTNO

GROUP BY ROLLUP (DNAME, JOB)

 

--MYSQL

SELECT     DNAME     ,JOB     ,COUNT(*) AS TOTAL_EMPL     ,SUM(SAL) AS TOTLA_SAL FROM EMP E, DEPT DP WHERE E.DEPTNO = DP.DEPTNO GROUP BY DNAME, JOB WITH ROLLUP

결과) MYSQL 쿼리문으로 돌린 결과

▲실행결과를 살펴보면 그룹핑 컬럼이 DNAME, JOB 에 대해 추가 LEVEL의 집계가 생성된 것을 볼 수 있음.

L1 - GROUP BY 수행시 생성되는 표준 집계 (9건)
L2 - DNAME 별 모든 JOB의 SUBTOTAL(3건)
L3 - GRAND TOTAL(마지막 행 1건)

 

참고) ROLLUP의 경우 계층 간 집계에 대해서는 LEVEL별 순서(L1 > L2 > L3)를 정렬하지만, 계층 내 GROUP BY 수행 시 생성되는 표준 집계(L1)에는 별도의 정렬을 지원하지 않음.

계층 내 정렬을 하고 싶으면 별도의 ORDER BY 절을 사용해야 됨.

 

STEP 2-2. ROLLUP 함수 + ORDER BY 절 사용

--ORACLE

SELECT

DNAME

,JOB

,COUNT(*) AS TOTAL_EMPL

,SUM(SAL) AS TOTLA_SAL

FROM EMP E, DEPT DP

WHERE E.DEPTNO = DP.DEPTNO

GROUP BY ROLLUP (DNAME, JOB) ORDER BY DANEM, JOB;

 

--MYSQL

SELECT     DNAME     ,JOB     ,COUNT(*) AS TOTAL_EMPL     ,SUM(SAL) AS TOTLA_SAL FROM EMP E, DEPT DP WHERE E.DEPTNO = DP.DEPTNO GROUP BY DNAME, JOB DESC WITH ROLLUP

STEP 3. GROUPING 함수 사용

ROLLUP, CUBE, GROUPING SETS 등 새로운 그룹 함수를 지원하기 위해 GROUPING 함수가 추가 됨.

- ROLLUP이나 CUBE에 의한 소계가 계산된 결과 : GROUPING(EXPR) = 1

- 그 외의 결과 : GROUPING(EXPR) = 0

- GROUPING 함수와 CASE/DECODE를 이용해, 소계를 나타내는 필드에 원하는 문자열을 지정 할 수 있음.

--ORACLE

SELECT

DNAME

,GROUPING(DNAME)

,JOB

,GROUPING(JOB)

,COUNT(*) AS TOTAL_EMPL

,SUM(SAL) AS TOTLA_SAL

FROM EMP E, DEPT DP

WHERE E.DEPTNO = DP.DEPTNO

GROUP BY ROLLUP (DNAME, JOB) ORDER BY DANEM, JOB;

결과)

 

▲실행결과를 살펴보면 그룹핑 함수 파라미터 컬럼에 대해 1을 리턴한 것을 확인할 수 있음.

마지막 합계의 경우 DNAME, JOB 둘 다 1이기 때문에 1이 한개인 경우는 소계, 1이 두개인 경우는 합계로 볼 수 있음.

 

STEP 4. GROUPING 함수 + CASE 사용

SELECT 

CASE GROUPING(DNAME) WHEN 1 THEN 'All Departments' ELSE DNAME END AS DNAME

,CASE GROUPING(JOB) WHEN 1 THEN 'All Jobs' ELSE JOB END AS JOB, COUNT(*) "Total Empl"

,SUM(SAL) "Total Sal"

FROM EMP, DEPT 

WHERE DEPT.DEPTNO = EMP.DEPTNO 

GROUP BY ROLLUP (DNAME, JOB); 

 

--Oracle의 경우는 DECODE 함수를 사용해서 좀더 짧게 표현할 수 있음.

-- 하지만 속도를 생각하면 CASE 문을 쓰는게 좋음.

SELECT

DECODE(GROUPING(DNAME), 1, 'All Departments', DNAME) AS DNAME

, DECODE(GROUPING(JOB), 1, 'All Jobs', JOB) AS JOB

, COUNT(*) "Total Empl"

, SUM(SAL) "Total Sal" 

FROM EMP, DEPT 

WHERE DEPT.DEPTNO = EMP.DEPTNO 

GROUP BY ROLLUP (DNAME, JOB);

결과)

 

STEP 4-2. ROLLUP 함수 일부 사용

SELECT 

CASE GROUPING(DNAME) WHEN 1 THEN 'All Departments' ELSE DNAME END AS DNAME

,CASE GROUPING(JOB) WHEN 1 THEN 'All Jobs' ELSE JOB END AS JOB

,COUNT(*) "Total Empl"

,SUM(SAL) "Total Sal" 

FROM EMP, DEPT 

WHERE DEPT.DEPTNO = EMP.DEPTNO 

GROUP BY DNAME, ROLLUP(JOB)

결과)

▲실행결과를 살펴보면 집계는 JOB만 사용했기 때문에 합계는 나타나지 않음.

 

3. CUBE 함수

 

-ROLLUP에서는 단지 가능한 Subtotal만을 생성하였지만, CUBE는 결합 가능한 모든 값에 대하여 다차원 집계를 생성함.

-CUBE를 사용할 경우에는 내부적으로는 Grouping Columns의 순서를 바꾸어서 또 한 번의 Query를 추가 수행해야 함. 

-Grand Total은 양쪽의 Query 에서 모두 생성이 되고 한 번의 Query에서는 제거되어야만 하므로 ROLLUP에 비해 시스템의 연산 대상이 많음.

-Grouping Columns이 가질 수 있는 모든 경우에 대하여 Subtotal을 생성해야 하는 경우에는 CUBE를 사용하는 것이 바람직

-ROLLUP에 비해 시스템에 많은 부담을 주므로 사용에 주의

-CUBE 함수의 경우 표시된 인수들에 대한 계층별 집계를 구할 수 있음.

-표시된 인수들 간에는 계층 구조인 ROLLUP과는 달리 평등한 관계이므로 인수의 순서가 바뀌는 경우 행간에 정렬 순서는 바뀔 수 있어도 데이터 결과는 같음

-CUBE도 결과에 대한 정렬이 필요한 경우는 ORDER BY 절에 명시적으로 정렬 칼럼이 표시가 되어야 함.

SELECT 

CASE GROUPING(DNAME) WHEN 1 THEN 'All Departments' ELSE DNAME END AS DNAME

, CASE GROUPING(JOB) WHEN 1 THEN 'All Jobs' ELSE JOB END AS JOB

, COUNT(*) "Total Empl"

, SUM(SAL) "Total Sal"

FROM EMP, DEPT

WHERE DEPT.DEPTNO = EMP.DEPTNO 

GROUP BY CUBE (DNAME, JOB) ;

결과)

▲실행결과를 살펴보면 GROUPING COLUMNS이 가질 수 있는 모든 경우의 수에 대하여 소계를 생성함.

ROLLUP함수 결과에서 업무별 집계를 표시한 5건의 레코드가 추가된 것을 확인할 수 있음.

SELECT DNAME, JOB, COUNT(*) "Total Empl", SUM(SAL) "Total Sal" FROM EMP, DEPT

WHERE DEPT.DEPTNO = EMP.DEPTNO GROUP BY DNAME, JOB 

UNION ALL

SELECT DNAME, 'All Jobs', COUNT(*) "Total Empl", SUM(SAL) "Total Sal" FROM EMP, DEPT

WHERE DEPT.DEPTNO = EMP.DEPTNO GROUP BY DNAME 

UNION ALL

SELECT 'All Departments', JOB, COUNT(*) "Total Empl", SUM(SAL) "Total Sal" FROM EMP, DEPT

WHERE DEPT.DEPTNO = EMP.DEPTNO GROUP BY JOB 

UNION ALL

SELECT 'All Departments', 'All Jobs', COUNT(*) "Total Empl", SUM(SAL) "Total Sal" FROM EMP, DEPT

WHERE DEPT.DEPTNO = EMP.DEPTNO

결과)

▲실행결과를 살펴보면 CUBE 사용했을때와 결과 데이터는 같지만 행들의 정렬이 다름.

CUBE 함수를 사용하면 UNIONALL을 쓰면서 네 번이나 반복 액세스 하는 부분을 한 번으로 줄일 수 있음.속도도 CUBE가 훨씬 빠름.

4.GROUPING SETS 함수

 

- GROUP BY SQL 문장을 여러번 반복하지 않고 소계 집합을 만들 수 있음.

- ROUPING SETS에 표시된 인수들에 대한 개별 집계를 구할 수 있음

- 표시된 인수들 간에는 계층 구조인 ROLLUP과는 달리 평등한 관계이므로 인수의 순서가 바뀌어도 결과는 같음. 

- 결과에 대한 정렬이 필요한 경우는 ORDER BY 절에 명시적으로 정렬 칼럼이 표시가 되어야 함.

--일반 그룹함수를 이용한 SQL SELECT  	DNAME 	, 'All Jobs' JOB 	, COUNT(*) "Total Empl" 	, SUM(SAL) "Total Sal"  FROM EMP, DEPT  WHERE DEPT.DEPTNO = EMP.DEPTNO  GROUP BY DNAME  UNION ALL  SELECT  	'All Departments' DNAME 	, JOB 	, COUNT(*) "Total Empl" 	, SUM(SAL) "Total Sal"  FROM EMP, DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO GROUP BY JOB ;

결과)

 

--GROUPING SETS을 이용한 SQL SELECT 

DECODE(GROUPING(DNAME), 1, 'All Departments', DNAME) AS DNAME

, DECODE(GROUPING(JOB), 1, 'All Jobs', JOB) AS JOB

, COUNT(*) "Total Empl", SUM(SAL) "Total Sal"

FROM EMP, DEPT 

WHERE DEPT.DEPTNO = EMP.DEPTNO

GROUP BY GROUPING SETS (DNAME, JOB);

결과)

 

SELECT 

DNAME

, JOB

, MGR

, SUM(SAL) "Total Sal" 

FROM EMP, DEPT 

WHERE DEPT.DEPTNO = EMP.DEPTNO

GROUP BY GROUPING SETS ((DNAME, JOB, MGR), (DNAME, JOB), (JOB, MGR)); 

--GROUPING SETS 함수 사용시 괄호로 묶은 집합별로(괄호 내는 계층구조가 아닌 하나의 데이터로 간주함) 집계를 구할 수 있다.

결과)

▲실행결과를 살펴보면 첫 번째 10건의 데이터는 (DNAME+JOB+MGR)에 대한 집계

두번째 8건의 데이터는 (JOB + MGR) 기준의 집계세번째 9건의 데이터는 (DNAME + JOB) 기준의 집계

반응형

'자격증 > SQLD(SQL 개발자)' 카테고리의 다른 글

SQLD - SQL 기본  (0) 2022.11.01
SQLD - 데이터 모델과 성능  (0) 2022.10.25
SQLD - 데이터 모델링의 이해  (2) 2022.10.24
SQLD(SQL 개발자) 요약/요점정리  (0) 2022.10.24
[SQL 활용] 서브쿼리  (3) 2017.12.06

댓글