07. PL/SQL 함수의 특징과 성능 부하
(1) PL/SQL 함수의 특징
SQL > show parameter plsql
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
plsql_ccflags string
*plsql_code_type string INTERPRETED*
plsql_compiler_flags string INTERPRETED, NON_DEBUG
(2) Recursive Call를 포함하지 않는 함수의 성능 부하
create or replace function date_to_char(p_dt date) return varchar2
as
begin
return to_char(p_dt, 'yyyy/mm/dd hh24:mi:ss');
end;
/
create table t ( no number, char_time varchar2(21) ) ;
set timing on
insert into t
select rownum no
, to_char(sysdate + rownum, 'yyyy/mm/dd hh24:mi:ss') char_time
from dual
connect by level <= 1000000 ;
1000000 개의 행이 만들어졌습니다.
경 과: *00:00:04.76*
insert into t
select rownum no
, date_to_char(sysdate + rownum) char_time
from dual
connect by level <= 1000000 ;
1000000 개의 행이 만들어졌습니다.
경 과: *00:00:25.60*
(3) Recursive Call를 포함하는 함수의 성능 부하
create or replace function date_to_char(p_dt date) return varchar2
as
n number;
begin
*select 1 into n from dual;*
return to_char(p_dt, 'yyyy/mm/dd hh24:mi:ss');
end;
/
insert into t
select rownum no
, date_to_char(sysdate + rownum) char_time
from dual
connect by level <= 1000000
/
1000000 개의 행이 만들어졌습니다.
경 과: *00:01:01.87*
SELECT 1
FROM DUAL
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1000000 9.12 8.39 0 0 0 0
Fetch 1000000 3.43 3.10 0 0 0 1000000
------- ------ -------- ---------- ---------- ---------- ---------- ----------
*total 2000001* 12.56 11.50 0 0 0 1000000
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 62 (recursive depth: 1)
(4) 함수를 필터 조건으로 사용할 때 주의 사항
-- emp 테이블을 읽어 평균 급여를 리턴하는 함수 생성
SQL> create or replace function emp_avg_sal return number
2 is
3 l_avg_sal number;
4 begin
5 select avg(sal) into l_avg_sal from emp;
6 return l_avg_sal;
7 end;
8 /
--컬럼 구성을 달리하는 index 4개 생성
SQL> create index emp_x10 on emp(sal);
SQL> create index emp_x20 on emp(deptno);
SQL> create index emp_x30 on emp(deptno, sal);
SQL> create index emp_x40 on emp(deptno, ename, sal);
*< 케이스 1 >*
- 인덱스를 사용하지 않고 Full Scan
SQL> select /*+ full(emp) */ * from emp
# where sal >= emp_avg_sal;
SELECT AVG(SAL) FROM EMP
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 14 0.00 0.00 0 0 0 0
Fetch 14 0.00 0.00 0 42 0 14
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 29 0.00 0.00 0 42 0 14
? Full Scan 할때 emp 테이블의 전체의 건수만큼 함수 호출이 일어났다
*< 케이스 2 >*
- sal 컬럼을 선두로 갖는 인덱스를 이용
SQL> select /*+ index(emp (sal)) */ * from emp
2 where sal > = emp_avg_sal;
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.00 0.00 0 3 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.00 0.00 0 3 0 1
? 함수 호출이 단 한번만 일어남
? 함수를 먼저 실행하고, 거기서 리턴된 값으로 emp_x10 인덱스를 액세스 하는 상수 조건으로 사용
*< 케이스 3 >*
- 조건절에 deptno 추가 deptno 컬럼 하나로 구성된 index 이용
SQL> select /*+ index(emp, emp_x20) */ * from emp
2 where sal >= emp_avg_sal
3 and deptno = 20;
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 5 0.00 0.00 0 0 0 0
Fetch 5 0.00 0.00 1 15 0 5
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 11 0.00 0.00 1 15 0 5
Predicate Information (identified by operation id):
---------------------------------------------------
# - filter("SAL">="EMP_AVG_SAL"())
# - access("DEPTNO"=20)
? deptno = 20 을 만족하는 레코드 5번
? sal >= emp_avg_sal 조건은 테이블 필터 조건으로 사용
*< 케이스 4 >*
- deptno +sal 순으로 구성된 emp_x30 인덱스 이용
SQL> select /* index(emp (deptno, sal)) */ * from emp
2 where sal >= emp_avg_sal
3 and deptno = 20 ;
Predicate Information (identified by operation id):
---------------------------------------------------
# - access("DEPTNO"=20 AND "SAL">="EMP_AVG_SAL"() AND "SAL" IS NOT NULL)
Call Count CPU Time Elapsed Time Disk Query Current Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse 1 0.000 0.000 0 0 0 0
Execute 1 0.000 0.000 0 0 0 0
Fetch 1 0.000 0.000 0 7 0 1
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total 3 0.000 0.000 0 7 0 1
- 함수 호출이 한번만 일어났다.
*< 케이스 5 >*
- deptno와 sal 중간에 ename 컴럼이 낀 emp_x40 인덱스 사용
SQL> select /*+ index(emp (deptno, ename, sal)) */ *
2 from emp
3 where sal >= emp_avg_sal
4 and deptno = 20;
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DEPTNO"=20 AND "SAL">="EMP_AVG_SAL"())
filter("SAL">="EMP_AVG_SAL"())
Call Count CPU Time Elapsed Time Disk Query Current Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse 1 0.000 0.001 0 0 0 0
Execute 6 0.000 0.000 0 0 0 0
Fetch 6 0.000 0.000 0 42 0 6
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total 13 0.000 0.002 0 42 0 6
- 인덱스 스캔할 첫 번째 레코드 액세스 단게에서 1번
- 필터 조건에서 4건을 찾는 동안 4번 (ename 정보가 없어서 필터 조건)
- One-plue (deptno = 20 범위를 넘어 더 이상 조건을 만족하는 레코드가 없음을 확인)
*< 케이스 6 >*
- '=' 조건이 아닌경우
- 인덱스 컬럼 구성상 선행 컬럼이 조건절에 누락되거나 = 조건이 아닌 경우 그 컬럼은 필터 조건으로 사용된다.
SQL> select /*+ index(emp (deptno, sal)) */ * from emp
2 where sal >= emp_avg_sal
3 and deptno >= 10 ;
2 - access("DEPTNO">=10 AND "SAL">="EMP_AVG_SAL"() AND "DEPTNO" IS NOT NULL)
filter("SAL">="EMP_AVG_SAL"()
Call Count CPU Time Elapsed Time Disk Query Current Rows
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Parse 1 0.000 0.000 0 0 0 0
Execute 14 0.000 0.001 0 0 0 0
Fetch 14 0.000 0.001 0 98 0 14
------- ------ -------- ------------ ---------- ---------- ---------- ----------
Total 29 0.000 0.002 0 98 0 14
- 인덱스 스캔할 첫번째 레코드를 엑세스하는 단계 1번
- Deptno >= 10 조건을 만족하는 나머지 13건 스켄 13번
- One-plus 스캔은 없음(맨 마지막 레코드에 도달했기 때문)
(5) 함수와 읽기 일관성
create table LookupTable ( key number, value varchar2(100) ) ;
insert into LookupTable values( 1, 'YAMAHA' );
commit;
create or replace function lookup(l_input number) return varchar2
as
l_output LookupTable.value%TYPE;
begin
select value into l_output from LookupTable where key = l_input;
return l_output;
end;
/
(6) 함수의 올바른 사용 기준