OpenSQL이 부족할 때
PP 실적 등 대량조회 -> ITAB COLLECT는 너무 느리다.
몇 건 안되면 상관이 없는데, 월간 10,000,000건 정도의 데이터를 일괄집계하려면
조회시간이 몇 분이 넘어가기 일쑤다.
방법이 없나 찾던 도중,
하나스튜디오에서 사용하는 HANA SQL을 샙구이를 통해 구현하는 방법을 찾았다.
SQL을 통짜 string으로 만들어 HANADB SQL EXECUTOR 오브젝트에 던져주면 된다.
SAP가 제시하는 데이터 처리 방법이 DB서버로 계산을 몰아주는 거고
인메모리DB가 빠르다곤 하나 별 체감을 못하고 있었는데,
오우.. 3분 이상 걸리던 조회가 10초 이내로 줄어들었다.
이런 SQL은 처음 써봐서 엉망진창일 것이 분명한데.....
아마 분명히 비효율의 끝판왕 같은 느낌일텐데도 향상폭이 드라마틱하구만.
며칠동안 이거 가지고 꼬물꼬물거렸는데 재밌는 것 같다.
data: lo_hdb_sql type ref to cl_hdb_sql_executor.
data: lo_dbsys type ref to cl_db6_sys.
lo_dbsys = cl_db6_sys=>get_sys_ref( exporting system_id = sy-sysid ).
lo_hdb_sql ?= cl_hdb_sql_executor=>get_instance( lo_dbsys ).
try.
lv_sql =
|select e.lotno as ablad, | &&
| b.aufnr, | &&
| c.werks, | &&
| b.matnr, | &&
| z.maktx, | &&
| c.dispo, | &&
| b.verid, | &&
| a.vornr, | &&
| f.ltxa1, | &&
* 오더 실적수량
| sum( case stzhl when '00000000' then a.lmnga | &&
| else a.lmnga * -1 end ) as lmnga, | &&
| sum( case stzhl when '00000000' then a.xmnga | &&
| else a.xmnga * -1 end ) as xmnga, | &&
* 오더 총 실적수량
| ( select sum( case when stzhl = '00000000' then lmnga | &&
| else lmnga * -1 end ) as lmnga_all | &&
| from afru | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
| ) as lmnga_all, | &&
| ( select sum( case when stzhl = '00000000' then xmnga | &&
| else xmnga * -1 end ) as xmnga_all | &&
| from afru | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
| ) as xmnga_all, | &&
* ZTECO = 'T' 인 레코드가 있는 오더는 완료
| case when ( select count( * ) | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and zteco = 'T' | &&
| and mtype = 'S' ) <> 0 then '{ lv_comp }' | &&
| else '{ lv_incomp }' | &&
| end as zteco, | &&
* 취소실적이 있는 경우 'X'
| case when ( select count( * ) | &&
| from afru | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
| and stokz = 'X' | &&
* budat in s_budat
| ) <> 0 then 'X' | &&
| else ' ' | &&
| end as zcanceled, | &&
* 취소실적수량만 별도집계
| sum( case stokz when 'X' then a.lmnga * -1 | &&
| else 0 end ) as lmnga_c, | &&
| sum( case stokz when 'X' then a.xmnga * -1 | &&
| else 0 end ) as xmnga_c, | &&
* ZPPR*_03/04 조회기간 양품실적 전송수량 (A,C)
| case f.ltxa1 when 'TT' then | &&
| ( select sum( case zflag when 'A' then erfmg4 | &&
| when 'C' then erfmg4 * -1 | &&
| end ) as erfmg1 | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
* budat in s_budat
| and { lv_where_sub } | &&
| ) | &&
| else | &&
| ( select sum( case zflag when 'A' then erfmg1 | &&
| when 'C' then erfmg1 * -1 | &&
| end ) as erfmg1 | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
* budat in s_budat
| and { lv_where_sub } | &&
| ) | &&
| end as lmnga_chk, | &&
* ZPPR*_03/04 조회기간 불량실적 전송수량 (B,D,X)
| case f.ltxa1 when 'TT' then | &&
| ( select sum( case zflag when 'B' then erfmg4 | &&
| when 'D' then erfmg4 * -1 | &&
| when 'X' then erfmg4 * -1 | &&
| end ) as erfmg1 | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
* budat in s_budat
| and { lv_where_sub } | &&
| ) | &&
| else | &&
| ( select sum( case zflag when 'B' then erfmg1 | &&
| when 'D' then erfmg1 * -1 | &&
| when 'X' then erfmg1 * -1 | &&
| end ) as erfmg1 | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
* budat in s_budat
| and { lv_where_sub } | &&
| ) | &&
| end as xmnga_chk, | &&
* ZPPR*_03/04 전체기간 양품실적 전송수량 (A,C)
| case f.ltxa1 when 'TT' then | &&
| ( select sum( case zflag when 'A' then erfmg4 | &&
| when 'C' then erfmg4 * -1 | &&
| end ) as erfmg1 | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
| ) | &&
| else | &&
| ( select sum( case zflag when 'A' then erfmg1 | &&
| when 'C' then erfmg1 * -1 | &&
| end ) as erfmg1 | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
| ) | &&
| end as lmnga_chk_all, | &&
* ZPPR*_03/04 전체기간 불량실적 전송수량 (B,D,X)
| case f.ltxa1 when 'TT' then | &&
| ( select sum( case zflag when 'B' then erfmg4 | &&
| when 'D' then erfmg4 * -1 | &&
| when 'X' then erfmg4 * -1 | &&
| end ) as erfmg1 | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
| ) | &&
| else | &&
| ( select sum( case zflag when 'B' then erfmg1 | &&
| when 'D' then erfmg1 * -1 | &&
| when 'X' then erfmg1 * -1 | &&
| end ) as erfmg1 | &&
| from ztabname | &&
| where aufnr = b.aufnr | &&
| and vornr = a.vornr | &&
| ) | &&
| end as xmnga_chk_all | &&
| from afru as a | &&
| join aufk as y | &&
| on a.aufnr = y.aufnr | &&
| join afpo as b | &&
| on y.aufnr = b.aufnr | &&
| join ztabname as e | &&
| on b.aufnr = e.aufnr | &&
| join afvc as f | &&
| on a.aufpl = f.aufpl | &&
| and a.aplzl = f.aplzl | &&
| join marc as c | &&
| on b.matnr = c.matnr | &&
| and y.werks = c.werks | &&
| join mara as d | &&
| on c.matnr = d.matnr | &&
| left outer join makt as z | &&
| on z.matnr = d.matnr | &&
| and z.spras = '{ sy-langu }' | &&
| where { lv_where } | &&
| group by a.mandt, | &&
| e.lotno, | &&
| b.aufnr, | &&
| c.werks, | &&
| b.matnr, | &&
| z.maktx, | &&
| c.dispo, | &&
| b.verid, | &&
| a.vornr, | &&
| f.ltxa1 | &&
| order by b.aufnr, | &&
| a.vornr |.
lo_hdb_sql->exec_query_dyn(
exporting im_system = lo_dbsys
im_statement = lv_sql
importing ex_structdescr = data(lo_struct)
ex_result_ref = data(lo_ref)
).
catch cx_root into data(lo_xref).
message s000(zmpp) with lo_xref->get_text( ) display like 'E'.
endtry.
아참, 이렇게 SQL을 사용할 경우 SELECT-OPTIONS를 전혀 사용할 수 없는 문제가 발생한다.
그래서 이 때는 SELOPT을 조건문으로 변환해주는 FM을 태워주도록 하자.
하지만 길이가 너무 길어질 경우(정확히는 im_statement의 STRING 길이가 28kb를 넘어갈 때)에는 SQL 실행이 불가능해지니, SELOPT에 너무 많은 값을 넣으면 곤란할 때가 생기는 것 같다.
사실 어지간히 리턴 데이터가 많은게 아니고서는 그냥 가져와서 DELETE WHERE 하는게 편한것 같기도 할 정도로 빠르다.
data: lv_where type string,
lt_range type rs_t_rscedst with header line.
define _range2where.
loop at &1.
lt_range-fnam = &2.
move-corresponding &1 to lt_range.
append lt_range. clear lt_range.
if &1 = 'ICP*'.
if lv_where is initial.
lv_where = | A.MANDT = '200' |.
else.
lv_where = lv_where && | AND A.MANDT = '200' |.
endif.
exit.
endif.
call function 'RSDS_RANGE_TO_WHERE'
exporting
i_t_range = lt_range[]
importing
e_where = lv_where.
endloop.
end-of-definition.
_range2where: s_budat 'BUDAT'.
if lv_where is initial.
lv_where_sub = | MANDT = '200' |.
else.
lv_where_sub = lv_where. "서브쿼리조건 별도 생성
endif.
_range2where: s_matnr 'MATNR',
s_verid 'VERID',
s_dispo 'DISPO',
s_aufnr 'AUFNR',
s_lotno 'ZELOTNUM'.
if p_werks is not initial.
lv_where = |c.werks = '{ p_werks }' AND | && lv_where.
endif.
replace all occurrences of: 'BUDAT' in lv_where with 'A.BUDAT',
'MATNR' in lv_where with 'B.MATNR',
'VERID' in lv_where with 'B.VERID',
'DISPO' in lv_where with 'C.DISPO',
'AUFNR' in lv_where with 'B.AUFNR',
'ZELOTNUM' in lv_where with 'E.ZELOTNUM'.