✅ Static SQL 과 Dynamic SQL 재정의
- Static SQL : SQL Repository 에 완성된 형태로 저장한 SQL이다.
- Dynamic SQL : SQL Repository에 불완전한 형태로 저장한 후 런타임 시 상황과 조건에 따라 동적으로 생성되도록 작성한 SQL이다.
✅ 바인드 변수 미사용을 예외적으로 인정하는 경우
- 배치 프로그램이나 DW, OLAP등 정보계 시스템에서 사용되는 Long Running쿼리
- 파싱 소요시간이 쿼리 총 소요시간에서 차지하는 비중이 매우 낮고, 수행빈도가 낮아 하드파싱에 의한 라이브러리 캐시 부하를 유발할 가능성이 적을 때
- OLTP성 애플리케이션이더라도 사용빈도가 매우 낮아 하드파싱에 의한 라이브러리 캐시 부하를 유발할 가능성이 없을 때
- 조건절 컬럼의 값 종류(Distinct Value)가 소수일 때, 특히 값 분포가 균일하지 않아 옵티마이저가 컬럼 히스토그램 정보를 활용하도록 유도하고자 할 때.
- 증권시장구분코드 = ( '유가', '코스닥', '주식파생', '상품파생' )
Static SQL에서 기본 원칙이 잘 지켜지지 않는 이유는 바로 ⭐️선택적 검색 조건 구현⭐️ 때문이다.
Static SQL을 지원하는 개발 환경에서조차 검색 조건이 다양해 자주 Dynamic SQL를 사용해 조건절을 동적으로 구성하게 된다.
사용자의 선택이나 입력 값에 따라 %option 부분에 조건절을 아래와 같이 동적으로 붙여나간다.
select 거래일자, 종목코드, 투자자유형코드, 주문매체코드 , 체결건수, 체결수량, 거래대금
from 일별종목거래
where 거래일자 between :시작일자 and :종료일자
%option ;
👉 %option = " and 종목코드 = 'KR123456' and 투자자유형코드 = '1000'"
select 거래일자, 종목코드, 투자자유형코드, 주문매체코드 , 체결건수, 체결수량, 거래대금
from 일별종목거래
where 거래일자 between :시작일자 and :종료일자
and 종목코드 = nvl(:종목코드, 종목코드) and 투자자유형코드 = nvl(:투자자유형코드, 투자자유형코드)
and 주문매체구분코드 = nvl(:주문매체구분코드, 주문매체구분코드) ;
1. 거래일자 between
2. 거래일자 between, 종목코드 =
3. 거래일자 between, 종목코드 =, 투자자유형코드 =
4. 거래일자 between, 종목코드 =, 투자자유형코드 =, 주문매체구분코드 =
5. 거래일자 between, 종목코드 =, 주문매체구분코드 =
6. 거래일자 between, 투자자유형코드 =
7. 거래일자 between, 투자자유형코드 =, 주문매체구분코드 =
8. 거래일자 between, 주문매체구분코드 =
< 인덱스 구성 >
INDEX01 : 종목코드 + 거래일자
INDEX02 : 투자자유형코드 + 거래일자 + 주문매체구분코드
INDEX03 : 거래일자 + 주문매체구분코드
select 거래일자, 투자자유형코드, 회원번호 , 체결건수, 체결수량, 거래대금
from 일별종목거래
where :종목코드 is not null
and 거래일자 between :시작일자 and :종료일자
and 종목코드 = :종목코드
and 투자자유형코드 = nvl(:투자자유형, 투자자유형코드)
and 주문매체구분코드 = nvl(:주문매체, 주문매체코드)
union all
select 거래일자, 투자자유형코드, 회원번호 , 체결건수, 체결수량, 거래대금
from 일별종목거래
where :종목코드 is null and :투자자유형 is not null
and 거래일자 between :시작일자 and :종료일자
and 투자자유형코드 = nvl(:투자자유형, 투자자유형코드)
and 주문매체구분코드 = nvl(:주문매체, 주문매체코드
union all
select 거래일자, 투자자유형코드, 회원번호 , 체결건수, 체결수량, 거래대금
from 일별종목거래
where :종목코드 is null and :투자자유형 is null
and 거래일자 between :시작일자 and :종료일자
and 주문매체구분코드 = nvl(:주문매체, 주문매체구분코드) ;
이 때 , 라이브러리 캐시 효율화의 핵심인 바인드 변수 사용 원칙만큼은 준수한다.
SQLStmt := 'SELECT 거래일자, 종목코드, 투자자유형코드, 주문매체코드, 체결건수, 체결수량, 거래대금 '
|| 'FROM 일별종목거래 '
|| 'WHERE 거래일자 BETWEEN :1 AND :2 ';
IF :종목코드 IS NULL Then
SQLStmt := SQLStmt || 'AND :3 IS NULL ';
Else
SQLStmt := SQLStmt || 'AND 종목코드 = :3 ';
End If;
If :투자자유형 IS NULL Then
SQLStmt := SQLStmt || 'AND :4 IS NULL ';
Else
SQLStmt := SQLStmt || 'AND 투자자유형코드 =:4 ';
End If;
EXECUTE IMMEDIATE SQLStmt
INTO :A, :B, :C, :D, :E, :F, :G
USING :시작일자, :종료일자, :종목코드, :투자자유형코드;
✅ 수행된 최종 SQL 수집 방법
- SQL 트레이스 조회
- v$sql 뷰 조회
- 등등 ^^
SELECT 거래일자, 종목코드, 투자자유형코드, 주문매체코드 , 체결건수, 체결수량, 거래대금
FROM 일별종목거래
WHERE 거래일자 BETWEEN ? AND ? ;
SELECT 거래일자, 종목코드, 투자자유형코드, 주문매체코드 , 체결건수, 체결수량, 거래대금
FROM 일별종목거래
WHERE 거래일자 BETWEEN ? AND ?
AND 종목코드 = ? 등등.. ;
select *
from 일별종목거래
where (:isu_cd is null or isu_cd = :isu_cd);
=====================================================
Execution Plan
---------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS
1 0 TABLE ACCESS (FULL) OF '일별종목거래' (TABLE)
=====================================================
select *
from 일별종목거래
where isu_cd like :isu_cd || '%';
=====================================================
Execution Plan
---------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS
1 0 TABLE ACCESS (BY LOCAL INDEX ROWID) OF '일별종목거래' (TABLE)
2 1 INDEX (RANGE SCAN) OF '일별 종목거래_PK' (INDEX (UNIQUE))
=====================================================
select *
from 일별종목거래
where isu_cd = nvl(:isu_cd, isu_cd) ;
===================================================================
Execution Plan
---------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS
1 0 CONCATENATION
2 1 FILTER
3 2 TABLE ACCESS (FULL) OF '일별종목거래' (TABLE)
4 1 FILTER
5 4 TABLE ACCCESS (BY LOCAL INDEX ROWID) OF '일별종목거래' (TABLE)
6 5 INDEX (RANGE SCAN) OF '일별종목거래_PK' (INDEX (UNIQUE))
===================================================================
select *
from 일별종목거래
where isu_cd = decode(:isu_cd, null, isu_cd, :isu_cd);
===================================================================
Execution Plan
---------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS
1 0 CONCATENATION
2 1 FILTER
3 2 TABLE ACCESS (FULL) OF '일별종목거래' (TABLE)
4 1 FILTER
5 4 TABLE ACCESS (BY LOCAL INDEX ROWID) OF '일별종목거래' (TABLE)
6 5 INDEX (RANGE SCAN) OF '일별종목거래_PK' (INDEX (UNIQUE))
===================================================================
✅ nvl 또는 decode 함수 사용시 주의할점
- null 허용 컬럼일 때는 결과 집합이 달라지므로 주의한다.
- 사용자가 :isu_cd 값을 입력하지 않았을 때는 조건절이 isu_cd = isu_cd가 되는데,
isu_cd 컬럼 값이 null일 때 오라클은 false를 리턴하기 때문이다.- 즉, null = null 비교가 불가능하다는 소리이다.
( 참고로, null = null 비교가 가능한 DBMS도 있기는 하다. )SQL> select * from dual where NULL = NULL; ============= 선택된 레코드가 없습니다. =============
select * from 일별종목거래 where :isu_cd is null
union all
select * from 일별종목거래 where :isu_cd is not null and isu_cd = :isu_cd
===================================================
Execution Plan
---------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS
1 0 UNION-ALL
2 1 FILTER
3 2 TABLE ACCESS (FULL) OF '일별종목거래' (TABLE)
4 1 FILTER
5 4 TABLE ACCESS (BY LOCAL INDEX ROWID) OF '일별종목거래' (TABLE)
6 5 INDEX (RANGE SCAN) OF '일별종목거래_PK' (INDEX (UNIQUE))
===================================================
👉 (:c isnull or cao = :c) 또는 (c like :c || '%') 등등 ...