11._Static_SQL_구현을_위한_기법들
2012.05.06 22:09
Dynamic SQL을 자주 사용하는 이유
. 조건절에 IN-List 항목이 가변적으로 변할때
(1) IN-List 항목이 가변적이지만 최대 경우 수가 적은 경우
>> Static 방식
select * from LP회원 where 회원번호 in ('01') select * from LP회원 where 회원번호 in ('01','02)
|
>>> Static SQl로 구현
select * from LP 회원 where 회원번호 in ( :a , :b, : c) |
>>> decode 문 사용시 ( 4개의 체크 박스에 각각 내부적으로 'all','01','02','03' 부여 되있다 가정)
select * from LP 회원 where 회원번호 in (decode (:a, 'all','01',':b') ,decode (:a, 'all','01',':c') ,decode (:a, 'all','01',':d') )
|
(2) IN-List 항목이 가변적이고 최대 경우 수가 아주 많은 경우
. 가능한 경우 수가 너무 많아 Static SQL로 일일이 작성해 두는 것은 불가능해 보임
>> 칼럼과 변수 위치를 서로 바꿈
select * from 수시공시내역 where 공시일자 = : 일자 and :inlist like '%' || 분류코드 || '%'
|
사용자가 선택한 분류코드를 'comma' 등 구분자로 연결해 아래 처럼 String 형 변수에 담아서 바인딩하고 실행함
: inlist := '01,03,08,14,17,24,33,46,48,53'
>>> 문자열을 처리하는 오라클 내부 알고리즘상 like 연산자보다 instr 함수를 사용하면 더 빠르므로 아래와 같이 권고
select * from 수시공시내역 where 공시일자 := 일자 and INSTR(:inlist, 분류코드) > 0
|
> index 구성에 따른 다른 사용의 예
1. IN-List 를 사용 할 때 select * from 수시공시내역 where 공시일자 = ;일자 and 분류코드 in (.....)
2. like 또는 instr 함수를 사용할 때 select * from 수시공시내역 where 공시일자 =: 일자 and INSTR(:inlist, 분류코드) > 0
|
. 인덱스 구성이 [분류코드 +공시일자] 일때
1번이 유리
2번 sql문은 인덱스를 사용하지 못하거나 Index Full scan 해야함
. 인덱스 구성이 [공시일자 + 분류코드] 일때
사용자가 선택한 항목의 개수가 소수일때는 1번이 유리,
하지만 인덱스를 그만큼 여러번 탐침해야 하므로 2번이 유리할 수도 있음 (유, 불리는 인덱스 깊이와 데이터 분포에 따라 결정됨)
. 인덱스 구성이 [분류코드 +공시일자] 일때
<인덱스를 효율적으로 액세스 할수 있는 방법1>
select /*+ ordered use_nl(B)*/ b.* from (select 분류코드 from 수시공시분류 where INSTR(:inlist, 분류코드) > 0 ) A , 수시공시내역 B where B.분류코드= A.분류코드
|
-> 수시공시분류 테이블은 100개 이내의 작은 테이블일 것이므로 Full Scan 으로 읽더라도 비효율은 없음
-> 작은 테이블을 Full Scan으로 읽으면서 NL조인 방식으로 분류코드 값을 수시공시내역 테이블에 던져줌
<인덱스를 효율적으로 액세스 할수 있는 방법2>
select /*+ ordered use_nl(B)*/ b.* from (select substr(:inlist, (rownum-1) *2+1,2) from 수시공시분류 where rownum <= length(:inli ) A , 수시공시내역 B where B.분류코드= A.분류코드
|
-> 드라이빙 집합으로 수시공시분류 테이블을 사용
이 SQL을 실행할 때는 사용자가 선택한 분류코드를 아래처럼 구분자 없이 연결해 String 형 변수에 담아서 바인딩함
:inlist := '01030814172433464853'
인라인뷰 A만 먼저 실행시켜 보면, 아래 10개 레코드를 갖는 집합이 됨
01
03
08
14
17
24
33
46
48
53
코드값 길이가 3자리면 숫자 2를 모두 3으로 변환하면 됨
<인덱스를 효율적으로 액세스 할수 있는 방법3>
select /*+ ordered use_nl(B)*/ b.* from (select substr(:inlist, (level-1) *2+1,2) from dual connet by level <= length(:inlist)/2)A , 수시공시내역 B where B.분류코드= A.분류코드
|
. 방법 2와 같은 방식, dual 테이블을 이용해 집합을 동적으로 생성
(3) 체크 조건이 가변적인 경우
> 주식종목에 대한 회원사별 거래실적을 집계하는 쿼리
select 회원번호, SUM(체결건수), SUM(체결수량), SUM(거래대금) from 일별거래실적 e where 거래일자 =: trd_dd and 시장구분 = '유가' and exists( select 'x' from 종목 where 종목코드 = e.종목코드 and 코스피종목편입여부 = 'Y' ) group by 회원번호
|
>>> Static SQL로 구현함
select 회원번호, SUM(체결건수), SUM(체결수량), SUM(거래대금) from 일별거래실적 e where 거래일자 =: trd_dd and 시장구분 = '유가' and exists( select 'x' from 종목 where 종목코드 = e.종목코드 and 코스피종목편입여부 = decode(:check_yn,'y','y',코스피종목편입여부) ) group by 회원번호
|
. 전종목을 대상으로 집계하고자 할때
--> 성능이 떨어짐 [Query : 8515]
. 사용자가 코스피 편입 종목만으로 집계하고자 할때
--> 통합하기 전과 블록 I/O가 같음, 2개 SQL로 분리하는 편이 낫음
>>> 굳이 하나의 SQL로 통합하고 싶다면 아래와 같이 함
select 회원번호, SUM(체결건수), SUM(체결수량), SUM(거래대금) from 일별거래실적 e where 거래일자 =: trd_dd and 시장구분 = '유가' and exists( select 'x' from dual where :check_yn = 'N' union all select 'x' from 종목 where 종목코드 = e.종목코드 and 코스피종목편입여부 = 'Y' and :check_yn = 'Y') group by 회원번호
|
--> exists 서브 쿼리는 존재 여부만 체크하므로 그 안에 union all을 사용하면 조인에 성공하는첫 번째 레코드를 만나는
순간 더는 진행하지 않고 true를 리턴
[전 종목을 대상으로 집계하고자 할 때]
--> 위 쿼리가 실행될 때 :check_yn 변수에 'N'값이 들어오면 , union all 아래쪽 종목 테이블과는 조인을 시도하지 않고
곧바로 exists 서브쿼리를 빠져나옴
10 g 부터 dual 테이블은 fast dual 방식으로 수행되기 때문에 i/o는 발생되지 않음
[사용자가 코스피 편입 종목만으로 집계하고자 할때]
--> 동일한 성능
(4) select-list가 동적으로 바뀌는 경우
/* 1: 평균 2: 합계 */ if ( pfmStrCmpTrim(INPUT->inData.gubun,"1",1) ==0){ snprint(...," avg(계약수), avg(계약금액), avg(미결제약정금액) "); } else { snprint(...," avg(계약수), avg(계약금액), avg(미결제약정금액) "); }
|
>>> Static SQL로 변환
/*1:평균 2: 합계 */ decode(:gubun, '1', avg(계약수), sum(계약수)), decode(:gubun, '1', avg(계약금액), sum(계약금액)), |
>>> 사례2
/* 1: 평균 2: 누적 */ if ( pfmStrCmpTrim(INPUT->inData.gubun,"1",1) ==0){ snprint(...," sum(계약수) 거래량, sum(거래대금) 거래대금 "); } else { snprint(...," sum(누적거래량) 거래량, sum(누적거래대금) 거래대금 "); }
|
>>> Static SQL로 변환
/* 1: 개별 2: 누적 */ decode(:gubun ,'1', sum(거래량), sum(누적거래량)) 거래량, decode(:gubun ,'1', sum(거래대금), sum(누적거래대금)) 거래대금 |
>>> 성능이 나빠지는 예 (decode 함수를 집계대상 건수만큼 반복 수행함)
/* 1: 개별 2: 누적 */ sum(decode(:gubun ,'1',거래량, 누적거래량)) 거래량, sum(decode(:gubun ,'1',거래대금, 누적거래대금)) 거래대금
|
(5) 연산자가 바뀌는 경우
비교연산자가 그때그때 달라지는 경우
>> 아래처럼 SQL 을 작성하고 바인딩하는 값을 바꾸면 됨
where 거래미형성률 between :min1 and :max1 and 일평균거래량 between :min2 and :max2 and 거래체결률 between :min7 and :max7 |
. 각 컬럼은 아래와 같은 도메인에 따라 표준화된 데이터 타입과 자리수 할당 받음
댓글 0
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
46 | 9._Static_vs._Dynamic_SQL | 남송휘 | 2012.05.07 | 5588 |
45 |
2._User_Call_vs._Recursive_Call
![]() | 정찬호 | 2012.05.07 | 4596 |
44 |
1._Call_통계
![]() | 정찬호 | 2012.05.07 | 4488 |
43 |
5장._데이터베이스_Call_최소화_원리
![]() | 운영자 | 2012.05.07 | 5116 |
» | 11._Static_SQL_구현을_위한_기법들 | dasini | 2012.05.06 | 3948 |
41 |
4._커서_공유
![]() | 남송휘 | 2012.04.26 | 16512 |
40 | 6._바인드_변수의_부작용과_해법 | 시와처 | 2012.04.22 | 4812 |
39 | 5._바인드_변수의_중요성 | 시와처 | 2012.04.22 | 4505 |
38 | 8._애플리케이션_커서_캐싱 | 박영창 | 2012.04.21 | 5438 |
37 | 7._세션_커서_캐싱 | 박영창 | 2012.04.21 | 8119 |
36 |
10._V$SQL
![]() | 정찬호 | 2012.04.09 | 6805 |
35 |
9._ASH(Active_Session_History)
![]() | 정찬호 | 2012.04.09 | 6288 |
34 | 4장._라이브러리_캐시_최적화_원리 | dasini | 2012.04.08 | 2938 |
33 |
12._데이터베이스_성능_고도화_정석_해법
![]() | 남송휘 | 2012.04.08 | 4744 |
32 | 11._End-To-End_성능관리 | 남송휘 | 2012.04.08 | 3240 |
31 | 3._라이브러리_캐시_구조 | dasini | 2012.04.05 | 5383 |
30 | 2._SQL_처리과정 | dasini | 2012.04.05 | 21945 |
29 | 1._SQL과_옵티마이저 | dasini | 2012.04.05 | 3824 |
28 | 5._V$SYSSTAT | AskZZang | 2012.04.03 | 4772 |
27 | 4._DBMS_XPLAN_패키지 | AskZZang | 2012.04.03 | 5773 |