- yagom's blog
- 배움에 길에는 끝이 없다.
- Naked Foot
- SAP PP
- SAP ABAP
- SAP BSP
- SAP Inside
- 자바지기
- SECRET OF KOREA
- X-Mobile User Interface World
- 대한민국 자식연합
- 대한민국 토리스토리
- Malus domestica
- PCPINSIDE(거리로 PC, 거실로 PC)
- My Eyes on You
- 조대협의 블로그
- 릴리펏's Logbook
- Dr. Ann(닥터앤)의 DB이야기
- 디지털을 말한다. By oojoo
- Slow Adopter
- T.B 의 SNS 이야기
- Sense and Sensibility
- 언제나 Burning~
- 바스토프의 세상이야기
- Edu&Story
- Min.Gun
- freestation
- nigh
- Programmer
- Shine A Light
- 하루 벌어 하루 살아요. ㅋㅋ
- 아이캐리즈
- 오라클 성능 문제에 대한 통찰 - 조동욱
- 에너쓰오라클
- Science of DataBase
- 기억을 글로 담기
- 홍기선's 아키텍트 이야기 그리고
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- semi join
- cluster table
- AWS Elastic Beanstalk
- Analytic Function
- Table
- oracle
- aws
- A2P
- JDBC
- ORACLE SQL
- Mean Time Between Failures
- Annualized Failure Rate
- java
- Network Neutrality
- MSSQL SQL
- PostgreSQL
- index-organized table
- zero rating
- ERP
- MTBF
- OUTER JOIN
- nested loops join
- Database
- 스폰서 요금제
- RBM
- ansi query
- data pump
- tuning
- EA
- oracle tuning
- Today
- Total
아이짱구
Analytic Function 본문
분석 함수는 Aggregate Function의 계산을 지정하는 행 그릅을 기반으로 계산하여 각 그룹에 대해 여러 행을 반환 할 수 있는 Function을 의미한다. 일반적으로 누적 계산, 집계 및 보고용 결과를 질의 할 때 유용하게 사용 할 수 있으며 복잡한 질의를 보다 간편하고 빠르게 실행 할 수 있게 도와준다.
SQL> SELECT empno
, ename
, sal
, deptno
, SUM(sal) AS dept_tot
FROM emp;
ERROR at line 1:
ORA-00937: not a single-group group function
위의 쿼리는 SUM 이라는 Aggregate Function(Group Function)을 GROUP BY 절 없이 일반 컬럼들과 함께 사용해서 발생한 오류이다.
다음 쿼리는를 보면,
EMPNO ENAME SAL DEPNO DEPT_TOT
----- ------ ---- ----- --------
7782 CLARK 2450 10 8750
7839 KING 5000 10 8750
7934 MILLER 1300 10 8750
7566 HONES 2975 20 10875
7902 FORD 3000 20 10875
7876 ADAMS 1100 20 10875
7369 SMITH 800 20 10875
7788 SCOTT 3000 20 10875
7521 WARD 1250 30 9400
7844 TURNER 1500 30 9400
7499 ALLEN 1600 30 9400
7900 JAMES 950 30 9400
7698 BLAKE 2850 30 9400
7654 MARTIN 2500 30 9400
결과는 동일하게 나온다.
그러나 불필요한 서브쿼리를 사용해야 하며, 원본 집합인 emp 테이블을 반복적으로 액세스하는 등 비효율적인 문장이 된다.
분석 함수는 원하는 결과를 가져다 주는 쿼리를 보다 쉽게 만들 수 있도록 도와주며, 성능 향상을 시켜주므로 그 쓰임새를 익혀둘 필요가 있다.
분석 함수 정의
EMPNO ENAME SAL AVG_SAL
----- ------ ---- --------
7369 SMITH 800 2073.21429
7499 ALLEN 1600 2073.21429
7521 WARD 1250 2073.21429
7566 JONES 2975 2073.21429
SQL> SELECT empno
, ename
, sal
, deptno
, job
, AVG(sal) OVER(PARTITION BY deptno, job) AS avg_sal
FROM emp; -- 부서별, 업무별 평균 급여
EMPNO ENAME SAL DEPTNO JOB AVG_SAL
----- ------ ---- ------ --------- --------
7934 MILLER 1300 10 CLERK 1300
7782 CLARK 2450 10 MANAGER 2450
7839 KING 5000 10 PRESIDENT 5000
7788 SCOTT 3000 20 ALANYST 3000
SQL> SELECT d.deptno
, d.dname
, e.ename
, e.sal
, AVG(e.sal) OVER(PARTITION BY d.deptno) AS avg_sal
FROM emp e
, dept d
WHERE d.deptno = e.deptno; -- 조인 후 부서별 평균 급여
DEPTNO DNAME ENAME SAL AVG_SAL
------ ---------- ------ ------ ----------
10 ACCOUNTING CLARK 2450 2916.66667
10 ACCOUNTING KING 5000 2916.66667
10 ACCOUNTING MILLER 1300 2916.66667
20 RESEARCH JONES 2975 2175
20 RESEARCH FORD 3000 2175
PARTITION BY 절을 사용 함으로 GROUP BY 절 없이 다양한 Grouping 집합의 집계 결과들을 함께 출력 할 수 있다,
ORDER BY 절은 PARTITION BY 절로 정의 된 WINDOW 내에서의 행들의 정렬 순서를 정의 한다.
SQL> SELECT empno
, ename
, sal
, deptno
, ROW_NUMBER() OVER(ORDER BY sal ASC) AS rnum
FROM emp; -- 전체 급여를 오름차순으로 정렬 했을 경우 Row Number
EMPNO ENAME SAL DEPTNO RNUM
----- ------ ------ ------ ----
7369 SMITH 800 20 1
7900 JAMES 950 30 2
7876 ADAMS 1100 20 3
7521 WARD 1250 30 4
SQL> SELECT empno
, ename
, sal
, deptno
, ROW_NUMBER() OVER(PARTITION BY deptno ORDER BY sal DESC) AS rnum
FROM emp; -- 부서별 급여를 내림차순으로 정렬 했을 경우 Row Number
EMPNO ENAME SAL DEPTNO RNUM
----- ------ ------ ------ ----
7839 KING 5000 10 1
7782 CLARK 2450 10 2
7934 MILLER 1300 10 3
7788 SCOTT 3000 20 1
7902 FORD 3000 20 2
부서번호가 바뀔 때 Row Number는 새로 시작 되는 것을 확인 할 수 있다.
SQL> SELECT empno
, ename
, sal
, comm
, DENSE_RANK() OVER(ORDER BY comm ASC) AS rnum
FROM emp
WHERE deptno = 30; -- NULL 값은 정렬 시 가장 큰 값으로 인식(기본 설정)
EMPNO ENAME SAL COMM RNUM
----- ------ ------ ------ ----
7844 TURNE 1500 0 1
7499 ALLEN 1600 300 2
7521 WARD 1250 500 3
7654 MARTIN 1250 1400 4
7900 JAMES 950 5
7698 BLAKE 2850 5
7782 CLARK 2450 5
SQL> SELECT empno
, ename
, sal
, comm
, DENSE_RANK() OVER(ORDER BY comm ASC NULL FIRST) AS rnum
FROM emp
WHERE deptno = 30; -- NULL 값을 가장 작은 값으로 설정
EMPNO ENAME SAL COMM RNUM
----- ------ ------ ------ ----
7900 JAMES 950 1
7698 BLAKE 2850 1
7782 CLARK 2450 1
7844 TURNE 1500 0 2
7499 ALLEN 1600 300 3
7521 WARD 1250 500 4
7654 MARTIN 1250 1400 5
ORDER BY 절은 PARTITION BY 절에 의해 그룹화 된 행들의 정렬 순서를 결정하며, NULL 값을 가지고 있는 행이 있을 경우 NULL에 대한 값을 FIRST, LAST로 보낼 수 있도록 조절 가능하다.
RANK(), DENSE_RANK(), ROW_NUMBER()의 차이점
SQL> SELECT empno
, ename
, deptno
, sal
, RANK() OVER(PARTITION BY deptno ORDER BY sal DESC) AS rank
, DENSE_RANK() OVER(PARTITION BY deptno ORDER BY sal DESC) AS d_rank
, ROW_NUMBER() OVER(PARTITION BY deptno ORDER BY sal DESC) AS rnum
FROM emp
WHERE deptno IN (10, 20);
EMPNO ENAME DEPTNO SAL RANK D_RANK RNUM
----- ------ ------ ------ ------ ------ ------
7839 KING 10 5000 1 1 1
7782 CLARK 10 2450 2 2 2
7934 MILLER 10 1300 3 3 3
7788 SCOTT 20 3000 1 1 1
7902 FORD 20 3000 1 1 2
7566 JONES 20 2975 3 2 3
7876 ADAMS 20 1100 4 3 4
7369 SMITH 20 800 5 4 5
현재 결과는 부서별 급여를 기준으로 내림차순으로 정렬 하였을 때 순번을 보여준다. 이때 RANK()는 동등 순위 발생 시 중복된 값만큼 증가 시킨 3이 표시되며, DENSE_RANK()는 동등 순위 번호는 같게 표시하며 다음 순위를 2로 표시한다.
즉, 중복된 값 만큼의 증가치는 없다는 것이며 ROW_NUMER()는 동등 순위 자체를 인식하지 않고 매번 증가되는 번호를 표시한다.
WINDOW 절은 일부 Aggregate Function과 함께 쓰일 수 있으며 행들의 그룹을 물리적, 논리적으로 조절하여 Function이 적용될 WINDOW 절을 정의한다. 즉, PARTITION BY 절은 컬럼에 같은 값을 기준으로만 그룹화를 시키지만, WINDOWING 절은 ROWS와 RANGE를 이용하여 하나의 WINDOW를 결정하는 범위를 보다 자유롭게 조정할 수 있다.
ROWS: 물리적인 단위 결정
SQL> SELECT empno
, ename
, sal
, SUM(sal) OVER(ORDER BY empno
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS sum_sal
FROM emp
WHERE deptno IN (10, 20);
EMPNO ENAME SAL SUM_SAL
----- ------ ------ -------
7369 SMITH 800 800
7566 JONES 2975 3775 <- 800 + 2975
7782 CLARK 2450 6225 <- 3775 + 2450
7788 SCOTT 3000 9225 <- ...
7839 KING 5000 14225 <- ...
7876 ADAMS 1100 15325 <- ...
7902 FORD 3000 18325 <- ...
7934 MILLER 1300 19625 <- 18325 + 1300
ROWS는 WINDOW 절의 범위를 정의 할 떄 물리적인 행을 지정하는 부분이다. 어떤 행에서 시작해서 어떤 행까지 하나의 WINDOW 영역으로 정의 할지 범위를 BETWEEN을 통하여 정의 할 수 있다. UNBOUNDED PRECEDING은 첫 번째 행을 가리키며 UNBOUNDED FOLLOWING은 마지막 행을 의미한다. CURRENT ROW는 현재 행을 의미한다.
그럼 위의 결과는 empno 순으로 오름차순 정렬한 집합에서 첫 번째 행부터 현재 행까지의 SUM(sal)을 계산하는 누적 집계를 보여준다.
아래의 문장은 동일한 결과를 보여준다.
SQL> SELECT empno
, ename
, sal
, SUM(sal) OVER(ORDER BY empno
ROWS BETWEEN UNBOUNDED PRECEDING) AS sum_sal
FROM emp
WHERE deptno IN (10, 20);
또는
SQL> SELECT empno
, ename
, sal
, SUM(sal) OVER(ORDER BY empno) AS sum_sal
FROM emp
WHERE deptno IN (10, 20);
즉, BETWEEN을 사용하지 않고 시작되는 행만을 지정하면 종료 행은 CURRENT ROW가 지정되게 된다.
BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING을 사용하면 첫 번째 행부터 마지막 행까지 하나의 WINDOW로 정의 할 수 있다.
SQL> SELECT empno
, ename
, sal
, SUM(sal) OVER(ORDER BY empno
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS sum_sal
FROM emp
WHERE deptno IN (10, 20);
EMPNO ENAME SAL SUM_SAL
----- ------ ------ -------
7369 SMITH 800 800
7566 JONES 2975 3775 <- 800 + 2975
7782 CLARK 2450 5425 <- 2975 + 2450
7788 SCOTT 3000 5450 <- ...
7839 KING 5000 8000 <- ...
7876 ADAMS 1100 6100 <- ...
7902 FORD 3000 4100 <- ...
7934 MILLER 1300 4300 <- 3000 + 1300
시작 행을 이전 행 (1 PRECEDING)으로 설정하였으므로 현재 행과 이전 행만의 합계를 구하는 문장이 된다.
RANGE는 논리적인 값을 근거로 WINDOW 범위를 설정 가능하게 된다.
SQL> SELECT ename
, TO_CHAR(hiredate, 'YYYY/MM/DD') hiredate
, sal
, SUM(sal) OVER(ORDER BY hiredate
RANGE BETWEEN INTERVAL '3' MONTH PRECEDING
AND INTERVAL '3' MONTH FOLLOWING) AS sum_sal
FROM emp
WHERE deptno IN (10, 20);
ENAME HIREDATE SAL SUM_SAL
------ ---------- ------- -------
SMITH 1980/12/17 800 800 <- 전후 3개월에 해당되는 값 없음
JONES 1981/04/02 2975 5425 <- 2975 + 2450 (04/02 ~ 06/09)
CLARK 1981/06/09 2450 5425 <- 2975 + 2450 (04/02 ~ 06/09)
KING 1981/11/17 5000 9300 <- 5000 + 3000 + 1300 (11/17 ~ 01/23)
FORD 1981/12/03 3000 9300 <- 5000 + 3000 + 1300 (11/17 ~ 01/23)
MILLER 1982/01/23 1300 9300 <- 5000 + 3000 + 1300 (11/17 ~ 01/23)
SCOTT 1987/04/19 3000 4100 <- 3000 + 1100 (04/19 ~ 05/23)
ADAMS 1987/05/23 1100 4100 <- 3000 + 1100 (04/19 ~ 05/23)
입사일자를 기준으로 정렬을 한 경우 현재 행이 가지고 있는 입사일자 전후로 3개월씩, 6개월 사이의 합계를 구한다. RANGE는 논리적인 평가를 통해 시작 행과 종료 행을 지정한다.
분석 함수 종류 (10g)
FIRST / LAST
각 WINDOW 영역에서 FIRST/LAST 행의 하나의 행만을 추출 하려는 경우 사용 가능하다. ORDER BY 절을 이용하여 WINDOW 내의 정렬을 진행하고 DENSE_RANK FIRST/LAST로 그들 중 하나의 행을 선택할 수 있다. 이때 동일한 Ranking을 가지고 있는 집합들 중 Aggregate Function의 결과를 보여준다.
-- 급여를 가장 많이 받는 사원과 가장 적게 받는 사원 이름
SQL> SELECT MIN(ename) KEEP(DENSE_RANK FIRST ORDER BY sal DESC) max_ename
, MIN(ename) KEEP(DENSE_RANK LAST ORDER BY sal DESC) min_ename
FROM emp;
MAX_ENAME MIN_NAME
--------- --------
KING SMITH
-- 부서별 급여를 가장 많이, 적게 받는 사원들의 이름
SQL> SELECT deptno
, MIN(ename) KEEP(DENSE_RANK FIRST ORDER BY sal DESC) max_ename
, MIN(ename) KEEP(DENSE_RANK LAST ORDER BY sal DESC) min_ename
FROM emp
GROUP BY deptno;
DEPTNO MAX_ENAME MIN_NAME
------ --------- --------
10 KING MILLER
20 FORD SMITH
30 BLAKE JAMES
NTILE
WINDOW 그룹의 행을 정렬 후 지정한 개수의 범위(등급)로 나눈 후 각 값이 가지고 잇는 등급 값을 보여준다.
-- 사원들의 급여를 많이 받는 순서대로 5개의 등급으로 나눠 표시
SQL> SELECT empno
, ename
, sal
, NTILE(5) OVER(ORDER BY sal DESC) AS grade
FROM emp;
EMPNO ENAME SAL GRADE
------ --------- -------- -----
7839 KING 5000 1
7902 FORD 3000 1
7788 SCOTT 3000 1
7566 JONES 2975 2
7698 BLAKE 2850 2
7782 CLARK 2450 2
7499 ALLEN 1600 3
7844 TURNER 1500 3
7521 WARD 1250 4
7654 MARTIN 1250 4
7900 JAMES 950 5
7369 SMITH 800 5
CUME_DIST(Cumulative distribution) / PERCENT_RANK
두 함수는 계산식에 차이가 있으나 WINDOW 그룹 내에서 누적 분포를 계산할 때 사용 가능하다. 값의 범위는 0 ~ 1까지 사용되며 PERCENT_RANK는 항상 시작 값이 0부터 시작된다.
-- 급여를 내림차순 기준으로 정렬하여 각 사원의 누적 분포 계산
SQL> SELECT empno
, ename
, sal
, ROUND(PERCENT_RANK() OVER(ORDER BY sal DESC), 2) AS per_rank
, ROUND(CUME_DIST() OVER(ORDER BY sal DESC), 2) AS cume_dist
, RANK() OVER(ORDER BY sal DESC) AS rank
, ROW_NUMBER() OVER(ORDER BY sal DESC) AS row_num
FROM emp;
EMPNO ENAME SAL PER_RANK CUME_DIST RANK ROW_NUM
------ --------- ------ -------- --------- ---- -------
7839 KING 5000 0 .07 1 1
7902 FORD 3000 .08 .21 2 2
7788 SCOTT 3000 .08 .21 2 3
7566 JONES 2975 .23 .29 4 4
7698 BLAKE 2850 .31 .36 5 5
7782 CLARK 2450 .38 .43 6 6
7499 ALLEN 1600 .46 .5 7 7
7844 TURNER 1500 .54 .57 8 8
7521 WARD 1250 .69 .79 9 9
7654 MARTIN 1250 .69 .79 9 10
PERCENT_RANK: (RANK - 1) / (COUNT(*) - 1)
CUME_DIST: (RANK or ROW_NUMBER) / COUNT(*)
동등 순위의 RANK 발생 시 해당 RANK의 마지막 ROW_NUMBER 사용
SQL> SELECT ROUND(1 - 1) / (14 - 1), 2) AS per, ROUND(1 / 14, 2) cume FROM dual UNION ALL
SELECT ROUND(2 - 1) / (14 - 1), 2) AS per, ROUND(3 / 14, 2) cume FROM dual UNION ALL
SELECT ROUND(2 - 1) / (14 - 1), 2) AS per, ROUND(3 / 14, 2) cume FROM dual UNION ALL
SELECT ROUND(4 - 1) / (14 - 1), 2) AS per, ROUND(4 / 14, 2) cume FROM dual UNION ALL
SELECT ROUND(5 - 1) / (14 - 1), 2) AS per, ROUND(5 / 14, 2) cume FROM dual;
PER CUME
--- ----
0 .07
.08 .21
.08 .21
.23. 29
.31 .36
PERCENTILE_CONT / PERCENTILE_DISC
CUM_DIST, PERCENT_RANK의 결과가 누적 분포도를 계산 한다면 PERCENTILE_CONT, PERCENTILE_DISC는 분포도 값(지정되는 백분율)을 역으로 계산하여 실제의 값을 가져온다.
-- 30번 부서번호를 가지고 있는 사원들의 급여을 기준으로 내림차순 정렬