1、抽取數據時,避免使用SELECT *, 儘量使用SELECT A B INTO TABLE ITAB這樣的語句。
2、不要使用SELECT...ENDSELECT語句。
3、 避免頻繁使用SELECT SINGLE語句, 特別是在LOOP和SELECT...ENDSELECT裏面用, 應該把要讀取的數據用SELECT FOR ALL ENTRIES IN 一次全部取得, 然後用READ TABLE WITH KEY ... BINARY SEARCH.
4、用SORT代替ORDER BY。
5、避免使用嵌套的循環。
6、儘量不要使用JOIN/INNDER JOIN 進行多表連接。不要超過3個表的連接。把一個表的數據先取到內表,然後使用FOR ALL ENTRIES語句再進行抽取。
7、使用二分查找法。
READ TABLE的之前使用SORT TABLE BY對內表進行排序, 然後使用READ TABLE WITH KEY ...BINARY SEARCH.
8、避免使用SELECT DISTINCT語句。在抽取數據到內表後用DELETE ADJACENT DUPLICATES語句來消除重複行。
9、儘量加多WHERE語句進行條件抽取。
以上,說的還不全,會進行不斷更新。
另外,可以通過TCODE:ST05 SE30 進行程序和SQL語句性能和效率的分析
首先是儘量減少I/O操作,類似對硬盤的讀寫的I/O操作是最耗費時間的, 比如讀寫數據庫。以下是減少I/O操作的例子:
1, 減少數據庫DB的讀寫操作, 當使用VIEW視圖的時候, 當被視圖join的table有數據更新操作的時候, 同時系統也會更新到這個view裏面, 使得它們之間的數據一樣, 所以使用視圖會對這些日常操作帶來效率問題. 如果視圖join的表多數是日常事物需要更新的事物數據表(如EKET), 就要避免使用視圖.
2, 避免使用SELECT *, 儘量使用SELECT A B C INTO TABLE ITAB這樣的語句。這個操作會將所有符合條件的數據一次性地讀進內表,這比在SELECT A B C INTO WA... APPEND... ENDSELECT的循環中添加數據到內表要快。不用頻繁的讀DB.
3, 避免頻繁使用SELECT SINGLE語句, 特別是在LOOP和SELECT...ENDSELECT裏面用, 應該把要讀取的數據用SELECT FOR ALL ENTRIES IN 一次全部取得, 然後用READ TABLE WITH KEY ... BINARY SEARCH.
雖然說操作內存比磁盤操作要高效,但是如果對內存的使用不加以控制,可能有些時候不得不對硬盤的交換空間操作, 這樣就增加了對磁盤的I/O讀寫操作.正如下面所說:
4, 當你定義內表的時候可以也會出現這樣的問題, 比如你定義一個內表使用的是OCCURS 100,而不是OCCURS 0, 會導致內表長度大於100的時候,就會佔用系統頁面緩存。
5, Field-groups對於多層次的排序和顯示是非常有用的。它是將數據寫入系統的頁面文件,而不是內存(內表一般是使用內存的)。基於這個原因,field-groups比較適合於處理大量數據的列表(一般超過50000條記錄)。如果涉及大量的數據處理,應該首先和系統管理員協商來決定這個程序最多能使用多少內存,以計算這個程序需要使用多少資源。然後你就可以決定是把數據寫入內存還是交換空間。
6, 用SORT代替ORDER BY, ORDER BY從句是執行在數據庫服務器上, 而SORT是ABAP語句執行在應用服務器上的. 數據庫服務器通常會形成性能瓶頸問題, 所以最好是吧數據導入內表做SORT.
7, 避免使用SELECT DISTINCT語句, 因爲當你用來判斷唯一的字段爲非索引字段時, 效率是十分的低, 所以請導入內表SORT後, 使用DELETE ADJACENT DUPLICATES 來去重複.
其次就是要減輕CPU的負載, 可以通過優化程序來改善,比如在程序中語句和算法, 以下是減低CPU負載的優化例子:
1, 使用宏代替頻繁函數調用. ABAP沒有內聯函數這個說法, 所以我們如果需要頻繁調用函數時, 函數調用有棧內存創建和釋放的開銷. 在ABAP中可以用宏代碼提高執行效率,宏代碼不是函數但使用起來像函數,編譯器用複製宏代碼的方式取代函數調用,省去了參數壓棧、從而提高速度。注意使用宏有缺點:(1)容易出錯, 宏不能pass-by-value按值傳遞,用於代替實現函數功能時要十分注意!(2)不可調試; (3)無法操作類的私有數據成員.
2, 避免使用過得的LOOP 和SELECT .... END SELECT. 避免使用嵌套的LOOP 和SELECT .... END SELECT.
3, 儘可能多地使用表的KEY FIELD作爲Where分句的條件選項。比如SELECT * FROM BSEG WHERE BUKRS = '1000' AND BELNR = '0100000007' AND GJAHR = '2006' AND BUZEI = '003'. 這裏的四個字段BUKRS,BELNR,GJAHR,BUZEI 都是BSEG表的KEY字段.
4, 如果某些數據需要頻繁的從不同表提取, 使用視圖VIEW實現讀取緩存可以提高效率. 當視圖連接的是讀取次數較多, 但寫入不頻繁的表時(比如物料主數據表MARA), 可以使用視圖, 這樣比在程序裏面簡單用join要快,理論上join語句每次讀取的速度都是一樣的, 而視圖是從讀二次開始就快了,而且cache使得網絡負載減低.????????? 什麼情況下定義視圖查詢快些????
5, 使用SQL語句裏面的JOIN時候, 應該避免JOIN的表不要超過3個, 否則嚴重影響效率.
要判斷For all entries in後面的內表是否爲空,如果它爲空的話,那麼在where條件中的與內表中字段進行比較的結果全部爲真,也就是全部滿足條件,這會導致取出非常多的數據,極大地影響系統的性能。
6, 注意使用CORRESPONDING FIELDS OF 和 MOVE-CORRESPONDING 時候會進行字段比較, 帶來CPU的開銷大.
7, 避免過得而頻繁的數據類型轉換,比如N轉換爲C,但是從N轉換成STRING卻是很快的,因爲操作STRING比CHAR類型少了比較長度的時間.
8, 使用二級索引提高DDIC的讀寫效率, 可以根據你的需要在SE11裏面創建INDEX, 並讓你程序裏的SQL查詢語句裏WHERE條件的順序與你的索引順序一致!! 注意嘍
9, 二分查找比線性查找要高效,READ TABLE的之前使用SORT TABLE BY XXX 某個表關鍵字段進行排序(注意:要升序排列!), 然後使用READ TABLE WITH KEY XXX = 'XXX' BINARY SEARCH. 這個就是所謂的二分查找法的應用.
10, 避免使用SQL語句動態查詢條件,動態表名和動態字段名, 必要時候用宏或者子程序模塊代替.
11, 對於同一功能的函數和方法, 調用METHOD比調用FUNCTION要快.
12, SORTED TABLE可以使用二分查找法取得節點, 其時間複雜度是O(log N),但是插入節點會慢,因爲要移動很多節點. 而HASHED TABLE則是基於哈希算法的,其高效主要體現在把數據的存儲和查找時間大大降低,幾乎可以看成是常數時間O(1),而代價是消耗比較多的內存,然而在硬件技術越來越發達的今天,用空間換時間的做法在某種意義上是值得的。但是使用哈希表必須注意鍵值的唯一性!如果鍵值會出現重複的話,不能使用哈希表,只能用排序表和標準表。
13, 使用APPEND LINES(或者INSERT LINES) OF ITAB1 TO ITAB2 比
14, 使用效率比較高的COLLECT, DELETE ADJACENT DUPLICATES FROM語句。
15, 使用高效的CONTEXT SQL語句.如以下代碼2比代碼1要快10倍以上!
代碼1:
SELECT * FROM SBOOK INTO SBOOK_WA UP TO 10 ROWS.
SELECT SINGLE AIRPFROM AIRPTO INTO (AP1, AP2)
FROM SPFLI
WHERE CARRID = SBOOK_WA-CARRID
AND CONNID = SBOOK_WA-CONNID.
SELECT SINGLE NAME INTO NAME1 FROM SAIRPORT
WHERE ID = AP1.
SELECT SINGLE NAME INTO NAME2 FROM SAIRPORT
WHERE ID = AP2.
ENDSELECT.
代碼2: 什麼語法啊 看不懂。。。。。
SELECT * FROM SBOOK INTO SBOOK_WA UP TO 10 ROWS.
SUPPLY CARRID = SBOOK_WA-CARRID
CONNID = SBOOK_WA-CONNID
TO CONTEXT TRAV1.
DEMAND AIRPFROM = AP1
AIRPTO = AP2
NAME_FROM = NAME1
NAME_TO = NAME2
FROM CONTEXT TRAV1.
ENDSELECT.
最後是注意內存的使用,以下是內存優化方面的例子:
1, 雖然ABAP擁有垃圾處理的機制, 但是這個是在程序運行完成後實現的. 所以我們儘量把無用的變量,內表,對象都釋放掉;
2, 儘量減少無用的靜態定義的變量,內表和對象, 因爲靜態定義的對象會在編譯開始就佔有內存空間;
3, 儘量減少網絡的傳輸負載, 比如在設計RFC遠程調用傳回的內表數據要儘量精簡, 數據量越大,對CPU和內存需求越多;
4, 內存使用緊張的情況下, 使用FREE語句, 以及SQL語句的PACKAGE SIZE n 選項.
SELECT vbeln erdat
FROM vbak
INTO TABLE li_vbak PACKAGE SIZE 50.
1、使用where語句 代替CHECK語句
不推薦
Select* from zflight.
Check : zflight-airln = ‘LF’ and zflight-fligh = ‘BW222’.
Endselect.
推薦
Select* from zflight where airln = ‘LF’ and fligh = ‘222’.
Endselect.
2、使用聚合函數
不推薦
Maxnu = 0.
Select* from zflight where airln = ‘LF’ and cntry = ‘IN’.
Check zflight-fligh >maxnu.
Maxnu = zflight-fligh.
Endselect.
推薦
Select max( fligh ) from zflight intomaxnu where airln = ‘LF’ and cntry = ‘IN’.
3、使用視圖代替基本表查詢 多用下
不推薦
Select* from zcntry where cntry like ‘IN%’.
Selectsingle * from zflight where cntry = zcntry-cntry and airln = ‘LF’.
Endselect.
推薦
Select* from zcnfl where cntry like ‘IN%’ and airln = ‘LF’.
Endselect.
4、使用INTO table 代替select endselect
不推薦
Refresh: int_fligh.
Select* from zflight into int_fligh.
Append int_fligh. Clear int_fligh.
Endselect.
推薦
Refresh: int_fligh.
Select* from zflight into table int_fligh.
5、使用批量修改內表代替逐行修改
不推薦
Loop at int_fligh.
If int_fligh-flag is initial.
Int_fligh-flag = ‘X’.
Endif.
Modify int_fligh.
Endloop.
推薦
Int_fligh-flag = ‘X’.
Modify int_fligh transporting flag where flag is initial.
當使用不帶表頭的內表時,需要從工作區刪除,例如
MODIFY IT_TEST1 FROM WA_TEST1 TRANSPORTING BOX WHERE BOX IS INITIAL.
6、使用二分法查詢,提高查詢內表數據速度
不推薦
Read table int_fligh with key airln = ‘LF’.
推薦
Read table int_fligh with key airln = ‘LF’ binary search.
7、兩個內表添加使用批量增加代替逐行
不推薦
Loop at int_fligh1.
Append int_fligh1 to int_fligh2.
Endloop.
推薦
Append lines of int_fligh1 to int_fligh2.
8、使用table buffering
Use of buffered tables is recommended to improve the performance considerably. The buffer is bypassed while using the following statementsSelectdistinct
Select… for update
Order by, group by, having clause
Joins
Use the Bypass buffer addition to theselectclause in order to explicitly bypass the buffer whileselecting the data.
9、 使用FOR ALL Entries
不推薦
Loop at int_cntry.
推薦
Select * from zfligh
For all entries in int_cntry
Where cntry = int_cntry-cntry.
10、正確地使用where語句,使查詢能使用索引When a base table has multiple indices, the where clause should be in the order of the index, either a primary or a secondary index
To choose an index, the optimizer checks the field names specified in the where clause and then uses an index that has the same order of the fields. One more tip is that if a table begins with MANDT, while an index does not, there is a high possibility that the optimizer might not use that index.
11、正確地使用MOVE語句
Instead of using the move-corresponding clause it is advisable to use the move statement instead. Attempt should be made to move entire internal table headers in a single shot, rather than moving the fields one by one.
12、正確地使用inner joinLet us take an example of 2 tables, zairln and zflight. The table zairln has the field airln, which is the airline code and the field lnnam, which is the name of the airline. The table zflight has the field airln, the airline code and other fields which hold the details of the flights that an airline operates.
Since these 2 tables a re logically joined by the airln field, it is advisable to use the inner join.
Selecta~airln a~lnnam b~fligh b~cntry into table int_airdet
From zairln as a inner join zflight as b on a~airln = b~airln.
In order to restrict the data as per theselection criteria, a where clause can be added to the above inner join.
13、使用sort by 代替order by
14、避免使用SELECT DISTINCT語句
使用ABAP SORT + DELETE ADJACENT DUPLICATES 代替.
15、內標1批量插入內標2.
INSERT LINES OF ITAB1 [FROM N1] [TO N2] INTO [TABLE] ITAB2 [INDEX IDX].
該語句要求兩內表對象具有可以相互轉換的行結構。如果不指定行數,則整個內表ITAB1被插入ITAB2中。不指定行數時,ITAB1可以爲任意內表,ITAB2必須爲索引表,但是如果需要指定起始行N1和終止行N2中的任意一個,則兩個內表都必須是索引表。如果ITAB2前指定TABLE 附加項,則ITAB2可以是任意類型內表。使用該方式將一個內表插入到另箇中的速度比用循環插入可以快20倍。
首先需要使用T-CODE:SE30 對程序執行時間分析,找出程序慢的部份,執行完程序可以區分三個階段的執行時間:ABAP、Database、System,這三個部份對效能的優先級爲Database -> System -> ABAP要依序排除程序這些部份的問題。
SE30使用方法:
點左上提示技巧,可以查看SAP所提供的一些效率比較。
輸入事務代碼、程序名稱、功能模塊名稱,點Execute,執行程序,執行完程序以後,點擊左下角Analyze,可以查看程序執行效率,
根據圖形對程序的執行進行分析,點左上角 ,可以查看詳細的每條語句的執行效率,
可以針對語句進行分析,如果語句執行時間過長,可以對語句或方法進行優化。
在SE30分析程序可以看出哪些Table花費的成本最高,針對程序中這些SQL語法進行檢查:首先要檢查索引,程序中的Where子句是否用到表索引,如果沒有Index,就要評估該報表執行頻率,如果很高就要建一個索引給它用。
這裏對效率比較低的語句,在網上找了一些資料,針對自己做的優化,以及SAP內自帶的一些優化方法,總結了一下。
1.最主要的是儘量減少I/O操作,然後是內存佔用,再就是CPU的負載。CPU的負載可以通過優化程序來改善,在程序中儘量使用諸如SUM(SQL語句)或者COLLECT(ABAP語句)。
2.儘可能多地使用表的索引作爲Where分句的條件選項,儘可能讓程序只讀取一定範圍內的記錄(比如說,你只准備操作一個月之內的業務數據,那麼對於這一個月的業務就應該有一定的範圍取值,如1000~2000)。
3.儘量使用Select A B C INTO TABLE ITAB這樣的語句。這個操作會將所有符合條件的數據一次性地讀進內表,這比在Select A B C INTO ITAB... ENDSELECT的循環中添加數據到內表要快。
4.儘可能使用Select SINGLE語句。
5.使用ABAP排序而不使用order by 。
6.可以使用視圖來代表基本表的查詢。
7.可以使用一些聚合函數、GROUP BY …HAVING,來進行計算和分組統計,也可以來改善查詢的效率。
例如:
不推薦
Maxnu = 0.
Select * from zflight where airln = ‘LF’ and cntry = ‘IN’.
Check zflight-fligh > maxnu.
Maxnu = zflight-fligh.
Endselect.
推薦
Select max( fligh ) from zflight into maxnu where airln = ‘LF’ and cntry = ‘IN’.
10.使用二分法查詢,提高查詢內表數據速度
不推薦
Read table int_fligh with key airln = ‘LF’.
推薦
Read table int_fligh with key airln = ‘LF’ binary search.
11.兩個內表添加使用批量增加代替逐行
不推薦
Loop at int_fligh1.
Append int_fligh1 to int_fligh2.
Endloop.
推薦
Append lines of int_fligh1 to int_fligh2.
12.使用FOR ALL Entries
不推薦
Loop at int_cntry.
Select single * from zfligh into int_fligh where cntry = int_cntry-cntry.
Append int_fligh.
Endloop.
推薦
Select * from zfligh appending table int_fligh
For all entries in int_cntry
Where cntry = int_cntry-cntry.
1 數據——>工作區,工作區——>內表,
2 數據——>內表
很明顯少了一個過程 效率自然高了 如果數據量越大,效果是可想而知的
13.避免使用SELECT DISTINCT語句
使用的 ABAP SORT + DELETE ADJACENT DUPLICATES 代替.
14. 更多地使用動態數據對象來訪問內表。
例:
不推薦:
LOOP AT itab.
READ TABLE itab_jest
WITH KEY objnr = itab-objnr.
ENDLOOP.
推薦:
FIELD-SYMBOLS: <ls_itab> TYPE typ_jhgb.
LOOP AT itab ASSIGNING <ls-itab>.
READ TABLE itab_jest
WITH KEY objnr = <ls-itab>-objnr.
ENDLOOP.
ABAP編程需要注意的小問題
一、Select語句中使用FOR ALL ENTRIES IN需要注意的問題
在ABAP編程中,使用FOR ALL ENTRIES IN是必不可少的語句,使用這個語句是先取出一些數據放到一個內表中,然後參考這張內表的數據取出其它的數據,這在ABAP開發中非常常用,例如:
DATA: BEGIN OF ig_bseg OCCURS 0,
werks LIKE bseg-werks,
belnr LIKE bseg-belnr,
gjahr LIKE bseg-gjahr,
dmbtr LIKE bseg-dmbtr,
END OF ig_mseg.
IF NOT ig_bkpf[] IS INITIAL.
SELECT werks belnr gjahr dmbtr
INTO CORRESPONDING FIELDS OF TABLE ig_bseg
FROM bseg
FOR ALL ENTRIES IN ig_bkpf
WHERE werks = ig_bkpf-werks
AND belnr = ig_bkpf-belnr
AND gjahr = ig_bkpf-gjahr.
ENDIF.
需要注意以下問題
1、首先,必須要判斷For all entries in後面的內表是否爲空,如果它爲空的話,那麼在where條件中的與內表中字段進行比較的結果全部爲真,也就是全部滿足條件,這會導致取出非常多的數據,極大地影響系統的性能。
2、對於上例,按照邏輯分析可以取出某個憑證的所有行項目,但是實際情況會與你預期的不一致,如果某個憑證的多個行項目的dmbtr值是完全一樣的,那麼在內表ig_bseg中你只會得到一行記錄,而不是多行,它自動使用了distinct,或者說刪除了重複的行,這是個非常致命的問題,會導致你的程序邏輯錯誤,而且很難以查找,解決的辦法就是要保證內表ig_bseg中取出的數據必須要有主鍵字段,在本例中,需要再添加buzei字段。
二、自建表和從系統外導入數據需要注意的問題
1、自建表中建立文本類型的字段(自己創建domain)時,需要注意是否允許字段可以保留文本的大小寫狀態,否則默認會全部轉換爲大寫字母。
2、在某個創建的自建表中,某個字段的數據元素爲MATNR(物料編號),類型爲CHAR(18),但是導入的數據的長度只有CHAR(12),比數據庫字段要短,如果直接導入,不進行任何處理。
2.1 首先導入數據肯定不會出現錯誤,但是在使用中會出現問題,例如我通過導入進去的物料編號找物料描述,是找不到數據的,因爲我們導入物料編號的長度只有12位,而系統標準表中的物料編號前面都是用0補齊18位的,所以我們在導入數據時,有些字段不能直接寫入數據庫,需要進行一定的處理。
2.2 我們可以調用系統的函數來實現補0這個功能。
CONVERSION_EXIT_ALPHA_INPUT 對話退出ALPHA,外部->內部 這個是補0函數
CONVERSION_EXIT_ALPHA_OUTPUT 對話退出ALPHA,內部->外部 這個是除0函數
函數調用非常簡單,如下
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = IG_UPLOAD-HKONT_NIS
IMPORTING
OUTPUT = WA_ZDFLNIS-HKONT_NIS
三、提高程序運行效率的一些小方法
1、我們在ABAP程序中定義的內表,在我們從數據庫中查詢數據的時候,查找數據的順序最好可以和內表定義的順序一樣,這樣可以提高效率的允許效率。
2、 我們在查找簇表數據的時候,例如BSEG表,對於一個簇表來說,除了主鍵項目外,其他項目都被編輯到一個長文本項目中,一起存儲在表簇RFBLG中的vardata項目中,這就決定了作爲簇表的BSEG無法再建立鍵值以外的索引(INDEX),而這張表對我們來說,很多報表程序要用到這張表,而如果直接用其他項目的字段查找BSEG表的數據,效率非常的底下,所以我們需要在另外的表中找到BSEG的主鍵項目,來加快查詢BSEG表的速度。
3、 SAP R/3系統中也有幾個專門用來讀取BSEG表信息的函數,可適當參考使用,它們是: READ_BSEG GET_ALL_BSEG
另外最有效率的方法是改善應用和需求,要使需求合理規範,這才能使效率達到最高化。