程序优化这个易学,但是难以达到一个完美的地步,我在日常联系中也不确定某段代码选择的优化写法是不是最好的,所以以下代表的我一些个人看法。
在具体介绍优化方法之前,先介绍两个工具,SAT(原SE30)和ST05,SAT用来分析程序效能问题,ST05来确定SQL是否用到了INDEX,SAT除了可以直接测前端,也可以在schedule中后台测试,但是后台的不能时间太大,否则超过一定范围会无效,
优化方法:
1、选择合适内表,例如在用DN+Item寻找Billing + Item、SO + Item寻找DN + Item的时候优先使用VBFA而不是LIPS/VBRP,在寻找工单发料的记录的时候优先使用AUFM而不是MSEG(注:即使mseg中有一些aufm没有的索引,但由于数据量远超过aufm,效能上依旧比不上)
2、SQL部分的优化:
1)、ST05中如果发现没有触发index,最好让建一个,但是不是什么table都能建,对那种数据量已经很大的table千万不要
2)、FOR ALL ENTRIES IN一定需要小心使用,虽然用起来挺方便的,效能上经常出问题:需要check以下是否为空,这个一般都不会在这个上面犯错;对for all entries in的对象行项目最好不要超过5000,因为如果用ST05去分析运行时的SQL,会发现其实这是用很多的OR去作为where条件去遍历这个数据库,所以一般出了效能问题就需要做一些调整,一般有两个方向,方向一:如果只用到entries in对象中的一个条件,可以改用range table的形式,但需要注意,range table不可以超过20000行,不然会发生dump;方向二:在DO循环中5000一批次做for all entries in ,
注:in range 其实是open sql的语法,在ST05中其实会翻译成native sql,这样range table里面的内容其实会翻译成多个 or 结合的语句,当超过20000行的时候,整个SQL语句占据的内存会超过限定。
IF gt_ywsdn_in_transit1[] IS NOT INITIAL.
CLEAR: lv_line,lv_time.
lv_line = lines( gt_ywsdn_in_transit1 ).
lv_time = ceil( lv_line / 4000 ).
DO lv_time TIMES.
REFRESH:gt_ywsdn_in_transit1_tmp.
lv_from = ( sy-index - 1 ) * 4000 + 1.
IF sy-index = lv_time.
lv_to = lv_line.
ELSE.
lv_to = sy-index * 4000.
ENDIF.
APPEND LINES OF gt_ywsdn_in_transit1 FROM lv_from TO lv_to TO gt_ywsdn_in_transit1_tmp.
SELECT lips~vbeln
lips~posnr
lips~matnr
lips~werks
lips~lfimg
lips~meins
lips~vgbel
lips~vgpos
likp~wadat_ist
likp~vkorg
APPENDING CORRESPONDING FIELDS OF TABLE lt_lips
FROM lips
INNER JOIN likp ON lips~vbeln = likp~vbeln
FOR ALL ENTRIES IN gt_ywsdn_in_transit1_tmp
WHERE lips~vbeln = gt_ywsdn_in_transit1_tmp-vbeln
AND lips~posnr = gt_ywsdn_in_transit1_tmp-posnr.
ENDDO.
ENDIF.
IF lr_likp[] IS NOT INITIAL.
SORT lr_likp BY low.
DELETE ADJACENT DUPLICATES FROM lr_likp COMPARING ALL FIELDS.
ls_likp = 'IEQ'.
MODIFY lr_likp FROM ls_likp TRANSPORTING sign option WHERE sign = space.
CLEAR:lr_likp_tmp,lv_line,lv_time.
lv_line = lines( lr_likp ).
lv_time = ceil( lv_line / 20000 ).
DO lv_time TIMES.
REFRESH:lr_likp_tmp.
lv_from = ( sy-index - 1 ) * 20000 + 1.
IF sy-index = lv_time.
lv_to = lv_line.
ELSE.
lv_to = sy-index * 20000.
ENDIF.
APPEND LINES OF lr_likp FROM lv_from TO lv_to TO lr_likp_tmp.
SELECT vbeln
posnr
parvw
kunnr
adrnr
APPENDING TABLE lt_vbpa
FROM vbpa
WHERE vbeln IN lr_likp_tmp
AND posnr = '000000'
AND parvw = 'WE'.
SELECT shorg
dono
cfm_date
APPENDING TABLE lt_yexpiv06
FROM yexpiv06
WHERE shorg IN shorg
AND dono IN lr_likp_tmp.
ENDDO.
3)、SQL中不要使用INTO CORRESPONDING FIELDS OF TABLE,尽量使用 INTO TABLE
4)、SQL使用EXISTS子查询对主查询进行过滤,
SELECT aufnr werks objnr INTO TABLE lt_aufk
FROM aufk
WHERE werks IN s_werks "modify by nelson 20161112
AND aufnr IN s_aufnr
AND loekz NE 'X'
AND EXISTS ( SELECT * FROM jest WHERE objnr = aufk~objnr AND inact NE 'X' AND stat = 'I0002' )
AND NOT EXISTS ( SELECT * FROM jest WHERE objnr = aufk~objnr AND inact NE 'X' AND stat = 'I0045' ).
5)、where 条件把空的range table放在后面,尽量按照index的先后循序摆放条件,<>、>、<、等条件最好放在后面,
6)、避免使用动态表名,动态字段名,避免使用distinct函数,
7)、强制使用索引%_hints_db6
8)、某些时候使用游标,或者native sql代替open sql,游标优点:可以直接对sql的每一条数据进行操作,而且可以使用for all entries in 和 open sql的range table,native sql,需要用perform 实现数据的操作,而且where条件不可以太复杂。
data:cur type cursor.
open cursor cur for select werks matnr aufnr menge meins bwart lgort from mseg
where werks in s_werks
and bwart in ('551','552','951','952','201','202')
and budat_mkpf in s_lfgja.
do.
fetch next cursor cur
into ls_itab_mseg.
if sy-subrc <> 0.
close cursor cur.
exit.
else.
endif.
enddo.
EXEC SQL PERFORMING loop_output .
select mandt, werks, vn_code, oa_code
into :ls_para
from ymsp_sup_para
where werks in ('PSC1','PSD1')
ENDEXEC.
loop at lt_para into ls_para.
write: / ls_para-mandt, ls_para-werks , ls_para-vn_code ,ls_para-oa_code.
endloop.
注:游标和native sql在使用的时候一定要声明client,否则会cross client。
3、内表操作:
1)、数据大的内表在使用结束之后,注意clear,减少内存
2)、善于利用和使用standard table、sorted table、hash table,hash在by key索引的时候效率最高,但是不能够append等操作,sorted table配合binary search效率上也还可以,但是同样不适合append 等操作,stardard table + sort + binary search效能上也比较客观,但是可以的话stardard table + secondary key效能更佳( 一个stardard table可以存在多种secondard key的排放形式 )
TYPES:BEGIN OF ts_prctr,
vgbel LIKE lips-vgbel,
vgpos LIKE lips-vgpos,
prctr LIKE vbap-prctr,
pctrf LIKE vbap-pctrf,
kwmeng LIKE vbap-kwmeng,
flag TYPE c, "hans add 20180330
END OF ts_prctr.
DATA:lt_prctr TYPE STANDARD TABLE OF ts_prctr
WITH UNIQUE SORTED KEY z1 COMPONENTS vgbel vgpos,
ls_prctr TYPE ts_prctr.
READ TABLE lt_prctr WITH TABLE KEY z1 COMPONENTS vgbel = <fs_detail>-vgbel
vgpos = <fs_detail>-vgpos INTO ls_prctr.
注:此时的系统变量sy-tabix不可用。
3)、loop循环的嵌套使用时:内部table需要定义为sorted table或者standard table + sort的形式,loop + read + loop from sy-tabix
LOOP AT it_matnr.
read table lt_bom with key werks = it_matnr-werks lead_matnr = it_matnr-matnr TRANSPORTING NO FIELDS BINARY SEARCH.
if sy-subrc = 0.
loop at lt_bom INTO ls_bom FROM sy-tabix.
IF ls_bom-werks <> it_matnr-werks OR ls_bom-lead_matnr <> it_matnr-matnr.
EXIT.
ENDIF.
endloop.
endif.
ENDLOOP.
4)、loop 的时候需要做modify ,可以用指针代替work area,
5)、read table trasporting no field / field_name可以提高效率
6)、loop 里面不要做delete,使用del_flag,然后批量操作
4、要使用好collect等函数
5、调用类方法快于function 函数,调用宏快于perform,
对于S4 HANA来说,HANA的计算能力十分强大,所以对于因为SQL运算引起的复杂计算导致的效能问题都可以通过 CDS View或者 HANA Calculation View来代替,Open SQL,这样CDS 或者CV他们在程序runtime的时候才会执行,计算能力比Open SQL要强大的多。