SQL 스칼라쿼리의 통합 방법
Updated:
예제 테이블 생성
오라클 11G DBMS를 설치하면 샘플 유저인 SCOTT 유저가 설치되는데 EMP 라는 테이블을 기준으로 테스트해봅니다.
Empno | ename | job | mgr | hiredate | sal | comm | deptno |
---|---|---|---|---|---|---|---|
7369 | SMITH | CLERK | 7902 | 1980/12/17 00:00:00 | 800 | 20 | |
7499 | ALLEN | SALESMAN | 7698 | 1981/02/20 00:00:00 | 1600 | 300 | 30 |
7521 | WARD | SALESMAN | 7698 | 1981/02/22 00:00:00 | 1250 | 500 | 30 |
7566 | JONES | MANAGER | 7839 | 1981/04/02 00:00:00 | 2975 | 20 | |
7654 | MARTIN | SALESMAN | 7698 | 1981/09/28 00:00:00 | 1250 | 1400 | 30 |
7698 | BLAKE | MANAGER | 7839 | 1981/05/01 00:00:00 | 2850 | 30 | |
7782 | CLARK | MANAGER | 7839 | 1981/06/09 00:00:00 | 2450 | 10 | |
7788 | SCOTT | ANALYST | 7566 | 1987/04/19 00:00:00 | 3000 | 20 | |
7839 | KING | PRESIDENT | 1981/11/17 00:00:00 | 5000 | 10 | ||
7844 | TURNER | SALESMAN | 7698 | 1981/09/08 00:00:00 | 1500 | 0 | 30 |
7876 | ADAMS | CLERK | 7788 | 1987/05/23 00:00:00 | 1100 | 20 | |
7900 | JAMES | CLERK | 7698 | 1981/12/03 00:00:00 | 950 | 30 | |
7902 | FORD | ANALYST | 7566 | 1981/12/03 00:00:00 | 3000 | 20 | |
7934 | MILLER | CLERK | 7782 | 1982/01/23 00:00:00 | 1300 | 10 |
- 생성 스크립트
CREATE TABLE EMP
(EMPNO NUMBER(4) NOT NULL,
ENAME VARCHAR2(10),
JOB VARCHAR2(9),
MGR NUMBER(4),
HIREDATE DATE,
SAL NUMBER(7, 2),
COMM NUMBER(7, 2),
DEPTNO NUMBER(2));
INSERT INTO EMP VALUES
(7369, ‘SMITH’, ‘CLERK’, 7902,
TO_DATE(‘17-12-1980’, ‘DD-MM-YYYY’), 800, NULL, 20);
INSERT INTO EMP VALUES
(7499, ‘ALLEN’, ‘SALESMAN’, 7698,
TO_DATE(‘20-02-1981’, ‘DD-MM-YYYY’), 1600, 300, 30);
INSERT INTO EMP VALUES
(7521, ‘WARD’, ‘SALESMAN’, 7698,
TO_DATE(‘22-02-1981’, ‘DD-MM-YYYY’), 1250, 500, 30);
INSERT INTO EMP VALUES
(7566, ‘JONES’, ‘MANAGER’, 7839,
TO_DATE(‘02-04-1981’, ‘DD-MM-YYYY’), 2975, NULL, 20);
INSERT INTO EMP VALUES
(7654, ‘MARTIN’, ‘SALESMAN’, 7698,
TO_DATE(‘28-09-1981’, ‘DD-MM-YYYY’), 1250, 1400, 30);
INSERT INTO EMP VALUES
(7698, ‘BLAKE’, ‘MANAGER’, 7839,
TO_DATE(‘01-05-1981’, ‘DD-MM-YYYY’), 2850, NULL, 30);
INSERT INTO EMP VALUES
(7782, ‘CLARK’, ‘MANAGER’, 7839,
TO_DATE(‘09-06-1981’, ‘DD-MM-YYYY’), 2450, NULL, 10);
INSERT INTO EMP VALUES
(7788, ‘SCOTT’, ‘ANALYST’, 7566,
TO_DATE(‘09-12-1982’, ‘DD-MM-YYYY’), 3000, NULL, 20);
INSERT INTO EMP VALUES
(7839, ‘KING’, ‘PRESIDENT’, NULL,
TO_DATE(‘17-11-1981’, ‘DD-MM-YYYY’), 5000, NULL, 10);
INSERT INTO EMP VALUES
(7844, ‘TURNER’, ‘SALESMAN’, 7698,
TO_DATE(‘08-09-1981’, ‘DD-MM-YYYY’), 1500, NULL, 30);
INSERT INTO EMP VALUES
(7876, ‘ADAMS’, ‘CLERK’, 7788,
TO_DATE(‘12-01-1983’, ‘DD-MM-YYYY’), 1100, NULL, 20);
INSERT INTO EMP VALUES
(7900, ‘JAMES’, ‘CLERK’, 7698,
TO_DATE(‘03-12-1981’, ‘DD-MM-YYYY’), 950, NULL, 30);
INSERT INTO EMP VALUES
(7902, ‘FORD’, ‘ANALYST’, 7566,
TO_DATE(‘03-12-1981’, ‘DD-MM-YYYY’), 3000, NULL, 20);
INSERT INTO EMP VALUES
(7934, ‘MILLER’, ‘CLERK’, 7782,
TO_DATE(‘23-01-1982’, ‘DD-MM-YYYY’), 1300, NULL, 10);
스칼라쿼리 통합
스칼라쿼리 통합 기술은 SQL 튜닝을 수행시 Select절에 있는 스칼라쿼리가 동일 테이블을 접근하고 있는경우 이를 서로 통합하여 SQL 부하흫 줄여주는 기술을 말합니다.
실전현장에서는 대단히 빠른 속도가 필요한 SQL들이 있는데 이런 경우 극한적인 튜닝기술이 필요하게 됩니다. 이때 스칼라쿼리가 서로 통합되어 있지 않으면 불필요한 부하를 발생시키므로 통합한 가능한 범위내에서 스칼라쿼리를 통합시켜주는것이 좋습니다.
튜닝대상 스칼라쿼리
이름, 월급, 사원 테이블의 최대월급, 최소월급, 전체월급을 현 월급을 많이 받는순으로 출력하시오
Select ename, sal,
(select max(sal) from emp) max_sal,
(select min(sal) from emp) min_sal,
(select sum(sal) from emp) sum_sal
From emp
Order by sal desc;
ENAME | SAL | MAX_SAL | MIN_SAL | SUM_SAL |
---|---|---|---|---|
KING | 5000 | 5000 | 800 | 29025 |
FORD | 3000 | 5000 | 800 | 29025 |
SCOTT | 3000 | 5000 | 800 | 29025 |
JONES | 2975 | 5000 | 800 | 29025 |
BLAKE | 2850 | 5000 | 800 | 29025 |
CLARK | 2450 | 5000 | 800 | 29025 |
ALLEN | 1600 | 5000 | 800 | 29025 |
TURNER | 1500 | 5000 | 800 | 29025 |
MILLER | 1300 | 5000 | 800 | 29025 |
WARD | 1250 | 5000 | 800 | 29025 |
MARTIN | 1250 | 5000 | 800 | 29025 |
ADAMS | 1100 | 5000 | 800 | 29025 |
JAMES | 950 | 5000 | 800 | 29025 |
SMITH | 800 | 5000 | 800 | 29025 |
Execution Plan
-------------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS
(Cost=4 Card=14 Bytes=140)
1 0 SORT (AGGREGATE) (Card=1 Bytes=4)
2 1 TABLE ACCESS (FULL) OF 'EMP' (TABLE)
3 0 SORT (AGGREGATE) (Card=1 Bytes=4)
4 3 TABLE ACCESS (FULL) OF 'EMP' (TABLE)
5 0 SORT (AGGREGATE) (Card=1 Bytes=4)
6 5 TABLE ACCESS (FULL) OF 'EMP' (TABLE)
7 0 SORT (ORDER BY) (Cost=4 Card=14 Bytes=140)
8 7 TABLE ACCESS (FULL) OF 'EMP' (TABLE)
튜닝 스칼라쿼리 1
모든 RDBMS에서 사용할수 있는 방법입니다. 대부분의 RDBMS들은 RPAD와 SUBSTR 함수를 제공합니다. 이것을 이용하여 간단하게 스칼라쿼리를 통합할수 있습니다.
통합시 실행계획을 보면 스칼라쿼리가 3번 동일테이블로 접근하는것이 1번 접근하는것으로 개선되었다.
select ename, sal,
substr(totalsal,1,10) max_sal,
substr(totalsal,11,10) min_sal,
substr(totalsal,21,10) sum_sal
from (select ename, sal,
(select rpad(max(sal),10,' ')||rpad(min(sal),10,‘ ‘)||
rpad(sum(sal),10,' ')
from emp ) totalsal
from emp)
order by sal desc;
Execution Plan
------------------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=4 Card=14 Bytes=140)
1 0 SORT (AGGREGATE) (Card=1 Bytes=4)
2 1 TABLE ACCESS (FULL) OF 'EMP' (TABLE)
3 0 SORT (ORDER BY) (Cost=4 Card=14 Bytes=140)
4 3 TABLE ACCESS (FULL) OF 'EMP' (TABLE)
튜닝 스칼라쿼리 2
다음은 오라클 11G 이상의 DBMS에서만 사용할수 있는 방법입니다. regexp_substr 함수를 이용하여 자리수를 원하는 만큼 끊어서 읽어 처리합니다.
Select ename, sal,
regexp_substr(tot, '[^/]+', 1, 1) as max_sal,
regexp_substr(tot, '[^/]+', 1, 2 ) as min_sal,
regexp_substr(tot, '[^/]+', 1, 3 ) as sum_sal
From (select ename, sal,
(select max(sal)||'/'||min(sal)||'/'||sum(sal) from emp) as tot
from emp)
Order by sal desc;
튜닝 스칼라쿼리 3
아래의 예제는 오라클의 row control 방법을 사용하여 통합한 예제입니다. 이방법은 스칼라쿼리내에 있는 테이블이 from절에 있는 테이블과 동일해야만 사용할수 있는 약점이 있습니다. 일반적으로 from절의 테이블과 스칼라쿼리내의 테이블이 틀릴경우가 많은데 이경우에는 안타깝게 이방법을 사용할수 없다. 그러나 테이블을 한번만 읽기 떄문에 성능은 가장 좋습니다고 생각됩니다.
Select ename, sal,
max(sal) over(order by sal rows between unbounded
preceding and unbounded following) max_sal,
min(sal) over(order by sal desc rows between unbounded
preceding and unbounded following) min_sal,
sum(sal) over(order by sal desc rows between unbounded
preceding and unbounded following) sum_sal
From emp
Order by sal desc;
Execution Plan
---------------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=14 Bytes=140)
1 0 WINDOW (SORT) (Cost=3 Card=14 Bytes=140)
2 1 TABLE ACCESS (FULL) OF 'EMP' (TABLE) (Cost=3 Card=14 Bytes=140)
튜닝 실전예제
실제현장에서 튜닝한 SQL입니다. 스칼라쿼리를 통합하여 성능을 개선하는 기술은 실전현장에서 많이 사용할수 있는 기술입니다. 매우 빈번하게 사용되는 SQL을 극한적으로 튜닝하여 성능을 개선할때 스칼라쿼리 통합의 관점에서도 검토해주길 바랍니다.
아래의 예제는 통합할 테이블의 칼럼을 인라인뷰에서 || 연결자와 ‘/’를 사용하여 구분하고 바깥쪽 스칼라쿼리에서 Regexp_substr 함수를 사용하여 원하는 자리수만큼 끊어 읽어서 스칼라쿼리에서 일어나는 테이블 접근을 최소화 하였습니다.
SELECT AA.HOUSE_NO,
(select regexp_substr(tot, '[^/]+', 1, 1) as CUSTOMER_NO from dual) CUSTOMERNO,
(select regexp_substr(tot, '[^/]+', 1, 2) as CUST_NAME from dual ) CUST_NAME,
aa.BUILD_CD, aa.BIL_BUILD_NAME,
(select regexp_substr(tot2, '[^/]+', 1, 1) as CENTER_ID from dual ) CENTER_ID,
(select regexp_substr(tot2, '[^/]+', 1, 2) as CUST_NAME from dual ) CENTER_NAME,
aa.DONG_HO, aa.ADDR, aa.ADDR_ALL, aa.ADDRESS_DESC, aa.LIVING_IN_DATE,
aa.LOOKUP_CODE_NAME, ROWNUM RNUM
from ( SELECT HI.HOUSE_NO,
(select CUSTOMER_NO||'/'||CUST_NAME
from EBL_CUSTOMER_INFO
where HI.CUSTOMER_NO = CUSTOMER_NO ) as tot ,
BM.BUILD_CD,
BM.BIL_BUILD_NAME,
(select CENTER_ID||'/'||CENTER_NAME
from EBL_CENTER_INFO ct
where BM.BIL_CENTER_ID = CT.CENTER_ID) as tot2
FROM table ………