사용자 정의 함수는
함수 호출 부하 해소 방안
함수를 인라인 뷰 안쪽에 두면 조건에 맞는 전체 레코드 수만큼 호출되고 정렬까지 발생한다.
해법은 맨 바깥 select-list로 끌어올린다. 그러면 order by + rownum 페이지 필터를 통과한 최종 10건에만 함수가 호출된다!
-- 함수를 맨 바깥으로 → 페이지된 최종 10건에만 호출
select memb_nm(매도회원번호) 매도회원명, ... -- 여기서만!
from (
select rownum no, a.* from (
select 매도회원번호, ... -- 안쪽은 원본 컬럼만, 함수 없음
from 체결 where ... order by 체결시각 desc
) a where rownum <= 30
) where no between 21 and 30;
함수 로직을 풀어서 decode, case문으로 전환하거나 조인문으로 구현할 수 있는지 확인해보자!
단계 A: 함수를 그냥 3번 호출
select sum(decode(SF_상품분류(시장코드, 증권그룹코드), '1. 주식 현물', 체결수량)) 주식현물수량
, sum(decode(SF_상품분류(시장코드, 증권그룹코드), '2. 주식외 현물', 체결수량)) 주식외수량
, sum(decode(SF_상품분류(시장코드, 증권그룹코드), '3. 파생', 체결수량)) 파생수량
from 체결
where 체결일자 = '20090315'
단계 B: 인라인 뷰로 묶기 (1번만 호출) -> 옵티마이저에 의해 A와 동일
select sum(decode(상품분류, '1. 주식 현물', 체결수량)) 주식현물수량
, sum(decode(상품분류, '2. 주식외 현물', 체결수량)) 주식외수량
, sum(decode(상품분류, '3. 파생', 체결수량)) 파생수량
from ( select SF_상품분류(시장코드, 증권그룹코드) 상품분류 -- 여기서 1번만 계산하려는 의도
, 체결수량
from 체결
where 체결일자 = '20090315' )
단계 C: no_merge 힌트 사용
select sum(decode(상품분류, '1. 주식 현물', 체결수량)) 주식현물수량
, sum(decode(상품분류, '2. 주식외 현물', 체결수량)) 주식외수량
, sum(decode(상품분류, '3. 파생', 체결수량)) 파생수량
from ( select /*+ NO_MERGE */ SF_상품분류(시장코드, 증권그룹코드) 상품분류
, 체결수량
from 체결
where 체결일자 = '20090315' )
select ( select d.dname -- 출력값: d.dname
from dept d
where d.deptno = e.deptno ) -- 입력값: e.deptno
from emp e
서브쿼리가 수행될 때마다 입력 값을 캐시에서 찾아보고 거기 있으면 저장된 출력 값을 리턴하고, 없으면 쿼리를 수행한 후 입력 값과 출력 값을 캐시에 저장한다.
함수를 Dual 테이블을 이용해 스칼라 서브쿼리로 한번 감싸준다!
특히, 함수 입력 값의 종류가 적을 때 이 기법을 활용하면 함수 호출 횟수를 획기적으로 줄일 수 있다!
select ...
from ( select /*+ NO_MERGE */
(select SF_상품분류(시장코드, 증권그룹코드) from dual) 상품분류 -- 감쌈
, 체결수량
from 체결 where 체결일자 = '20090315' )
입력값의 종류가 많으면 해시 충돌 때문에 함수가 그대로 호출된다. (충돌이 나면 오라클은 기존 캐시 엔트리를 그대로 둔 채 그냥 스칼라 서브쿼리를 한 번 더 실행한다)
10gR2에서 함수를 선언할 때 Deterministic 키워드를 넣어 주면 스칼라 서브쿼리를 덧입히지 않아도 캐싱 효과가 나타난다.
함수의 입력 값과 출력 값은 CGA(Call Global Area)에 캐싱된다.
CGA에 할당된 값은 데이터베이스 Call 내에서만 유효하므로 Fetch Call이 완료되면 그 값들은 모두 해제된다. 따라서 Deterministic 함수의 캐싱 효과는 데이터베이스 Call 내에서만 유효하다.
무엇보다 Deterministic은 오라클의 보장이 아니라 개발자의 선언일 뿐이어서 내부에 SELECT가 있는 함수에 캐싱 목적으로 붙이면 일관성 없는 결과를 초래하므로 순수 연산 함수에만 사용해야 한다.