ABAP开发规范













ABAP开发规范


 


目录
1 ABAP对象命名规范 6
1.1 开发类 6
1.2 程序名 6
1.3 数据字典 7
1.3.1 表/结构/数据元素/域 7
1.3.2 表类型 7
1.3.3 范围表类型 7
1.3.4 搜索帮助 7
1.3.5 锁对象 7
1.4 函数组及函数模块 8
1.4.1 函数组 8
1.4.2 函数模块 8
1.5 BADI实施 8
1.6 消息类 9
1.7 9
1.8 SMARTFORM 9
1.9 自定义权限对象 9
1.10 Enhancement project 9
1.11 Web Service命名 9
1.12 Proxy 命名 10
2 通用ABAP代码规范 11
2.1 代码格式 11
2.1.1 Pretty Printer 11
2.1.2 Spacing 11
2.1.3 代码行 12
2.1.4 代码注释 13
2.2 程序变量 14
2.2.1 变量使用说明 14
2.2.2 命名规范 14
2.2.3 常量 15
2.2.4 全局变量 17
2.2.5 内表/结构 18
2.2.6 文本变量 20
2.3 常用语句格式 21
2.3.1 Message 21
2.3.2 Call function 21
2.3.3 Perform 22
2.3.4 Select 22
2.3.5 IF 23
2.3.6 CHECK 24
2.3.7 LOOP 25
2.3.8 READ 25
2.3.9 WHILE 25
2.3.10 DO 26
2.3.11 CASE 26
2.3.12 CONCATENATE 27
3 报表程序规范 29
3.1 程序结构 29
3.1.1 数据初始化子程序 31
3.1.2 数据存取子程序 32
3.1.3 数据处理子程序 32
3.1.4 数据组合子程序 33
3.1.5 数据输出子程序 35
4 子程序规范 36
4.1 模块化代码 36
4.2 参数 37
4.2.1 参数命名 37
4.2.2 参数类型 37
4.2.3 Table参数 37
4.2.4 参数个数要求 38
4.2.5 参数对齐 40
4.3 子程序结构 40
4.4 Perform语句 41
4.5 子程序注释 42
5 功能模块规范 43
5.1 函数模块结构 43
5.2 函数返回值 43
5.3 函数调用 43
6 屏幕对话程序规范 45
6.1 屏幕逻辑流结构 45
6.2 PAI/PBO/POV 模块 46
6.2.1 User-command模块 47
7 BADI增强规范 49
8 CMOD增强规范 51
9 BTE增强规范 52
10 程序性能相关代码规范 53
10.1 SQL语句 53
10.2 内表操作 58
10.3 其他语句 66
10.3.1 CASE语句 66
10.3.2 WHILE语句 67
10.3.3 比较语句 67
附录 68
1.1 SAP业务模块命名表 68
1.2 数据字典对象类型命名表 68




版本控制
版本
更改日期
更改人
更改内容
1.0.
2011/XX/XX


创建文档












































ABAP对象命名规范
开发类
Z<XX>_<Short text>
<XX>-业务模块,见附录1
<Short text>-简短描述,英文
程序名
Z<XXY>_<Short text>
<XX>-业务模块,见附录1
<Y>-程序类型代码
<Short text>-简短描述,英文
<Y>
程序类型代码
R
报表程序
P
对话程序
F
FORM/SMARTFORM打印程序
B
后台作业程序
I
公共Include程序
注:用于特定程序的include程序按以下方式命名:<主程序名>_<Xnn>, nn-两位流水码
<X>
include类型
F
子程序
I
PAI
O
PBO


数据字典
表/结构/数据元素/域
Z<TXX>_<Short text>
<T>-数据字典对象类型,参见附表2
<XX>-业务模块,见附录1
<Short text>-简短描述,英文
表类型
[Z_]<structure >_T
[Z_]-如果< structure >名称以Z开头,则可省略
<structure>-使用的结构名
范围表类型
[Z_]<dataelement>_T
[Z_]-如果<dataelement>名称以Z开头,则可省略
<dataelement>-使用的数据元素名
搜索帮助
Z<XX>_SH_<Short text>
<XX>-业务模块,见附录1
<Short text>-简短描述,英文
锁对象
E[Z] <tablename>
[Z]-如果<tablename>名称以Z开头,则可省略
<tablename>-表名
函数组及函数模块
函数组
Z<XX>_<nn>
<XX>-业务模块,见附录1
<nn>-两位流水码
函数模块
<FunctionGroup>_<Short text>
<FunctionGroup>-函数模块所在的函数组名
< Short text >-函数基本功能的短文本
BADI实施
a)  Custom Business Add-in
Z<XX>_BADI_< Short text >
<XX>-业务模块,见附录1
< Short text >-BADI 定义的名称


b)  Custom Business Add-in Implementation
Z<XX>_BADIM_< Short text >
<XX>-业务模块,见附录1
< Short text >-BADI 定义的名称


c)  SAP Business Add-in Implementation
Z<XX>_IMPL_< Short text >
<XX>-业务模块,见附录1
< Short text >-BADI 定义的名称


d)  SAP BADI Method
< Short text >-自由定义,但有意义,简单描述该方法实现的功能


消息类
Z<XX>_<nn>
<XX>-业务模块,见附录1
< Short text >-简短描述,英文

Z<XX>_<Short text>
<XX>-业务模块,见附录1
<Short text>-简短描述,英文
SMARTFORM
[Z]<Reportname>_<Short text>
[Z]-如果<Reportname>名称以Z开头,则可省略
<Short text>-简短描述,英文
自定义权限对象
Z<XX>_<Short text>
<XX>-业务模块,见附录1
<Short text>-简短描述,英文
 Enhancement project
Z<XX><nnnn>
<XX>-业务模块,见附录1
<nnnn>-四位流水码
 Web Service命名
ZWS<XX>_<Short text>
<XX>-业务模块,见附录1
<Short text>-简短描述,英文


 Proxy 命名 
Z<XX>_<Short text>
<XX>-业务模块,见附录1
<Short text>-简短描述,英文


通用ABAP代码规范
代码格式
Pretty Printer
所有程序代码应使用pretty printer功能进行规范,包含:代码缩进;关键字大写,其余小写。


Unacceptable:
form f_list_output.
data: wa_output type ty_output.


if i_output[] is initial.
message e101(zcs01).
endif.
loop at i_output into wa_output.
write: / wa_output-equnr.
endloop.
endform.


Acceptable:
FORM f_list_output.
  DATA: wa_output TYPE ty_output.  "work area for output


  IF i_output[] IS INITIAL.
    MESSAGE e101(zcs01). "error no data
  ENDIF.


  LOOP AT i_output INTO wa_output.
    WRITE: / wa_output-equnr.
  ENDLOOP.
ENDFORM.                    "f_list_output


Spacing
在代码行中按照代码块适当增加空行,以增加代码的可读性:
Unacceptable:
form sub_display_print_document tables pi_ropbel type ty_tab_opbel.
    data: l_type   type c.  “print type
    data: lw_opbel type ty_opbel.  “Loop work area
    l_type = c_print_document.
    format color col_blue on.
    write: / text-h01.  “'Print documents (click to view)'.
    format color off.
    if not pi_ropbel[] is initial.
      loop at pi_opbel_disp into lw_opbel_disp.
        write: / lw_opbel-partner.
      endloop.
    endif.
    clear l_type.
endform.                    " display_print_document


Acceptable:
FORM sub_display_print_document TABLES pi_ropbel TYPE ty_tab_opbel.


  DATA: l_type   TYPE c.      "print type
  DATA: lw_opbel TYPE ty_opbel.   "Loop work area


  l_type = c_print_document.


  FORMAT COLOR COL_GROUP ON.
  WRITE: / text-h01.  "'Print documents (click to view)'.
  FORMAT COLOR OFF.


  IF NOT pi_ropbel[] IS INITIAL.
    LOOP AT pi_opbel_disp INTO lw_opbel_disp.
      WRITE: / lw_opbel-partner.
    ENDLOOP.
  ENDIF.


  CLEAR l_type.
ENDFORM.                    " display_print_document
代码行
每一行代码仅允许有一个语句
Unacceptable:
Sort i_tab1 by contract.  Sort i_tab2 by anlage.
Clear: lwa_tab1, lwa_tab2.

Acceptable:
Sort i_tab1 by contract.  
Sort i_tab2 by anlage.
Clear: lwa_tab1, “Clear work areas for processing
   lwa_tab2. “them in the loop.
代码注释
代码行中的注释,应该与代码行对齐。
Unacceptable:
form f_output_data.
*Print the headings
perform f_write_column_heading.


*** Loop at output table
Loop at i_output into w_output.
*Check if the document is reverse.
If  w_output-reverse   = c_reversed “Reversed state
and w_output-simulated = c_unsimluated.   ”simulated
Endif.
Endloop.
Endform.


Acceptable:
form f_output_data.
* Print the headings
perform f_write_column_heading.


* Loop at output table
Loop at i_output into w_output.
* Check if the document is reverse.
If  w_output-reverse   = c_reversed  “Reversed state
and w_output-simulated = c_unsimluated.   ”simulated
Endif.
Endloop.
Endform.
程序变量
变量使用说明
基本原则:程序中尽量减少全局变量的使用,如无必要均应使用局部变量,以保证程序中各代码段的相对独立性。全局变量一般用于:跨子程序使用的内表、常量、屏幕变量等。
变量声明:局部变量声明语句必需放在子程序的开始位置;不允许在一行代码中定义两个或以上的变量;在同一个data语句中的所有变量,应该让变量名对齐、type〔like〕对齐。
变量使用:原则上每个变量仅为一种用途使用,尽量避免一个变量被当作不同的含义重复使用。
Unacceptable:
    data: l_cnt type i, l_txt_prefix(10) type c.
    data : l_curr_div like eanl-sparte.
Acceptable:
    data: l_prefix(3)  type c. “Prefix of operand
      l_cnt         type i, “Count of material docs.
           l_curr_div    like eanl-sparte. “Current division
命名规范
变量:<A><B>_<description>
<A>-代表变量的使用范围
<B>-代表变量类别
<A>
使用范围
G
全局变量
L
局部变量
C
全局常量
I
形式参数-传入
E
形式参数-传出


<B>
变量类别
V
简单变量
T
内表变量
S
结构变量
F
flag
R
range
类型:TY_<description>


Example:
TYPES: BEGIN OF ty_po_header,
Ebeln TYPE ekko-ebeln, “PO NUMBER
Lifnr TYPE ekko-lifnr, “VENDOR ID
  END OF ty_po_header.


CONSTANTS: BEGIN OF cs_po_type,
normal  TYPE ekko-bsart VALUE ‘PO1’,
special TYPE ekko-bsart VALUE ‘PO2’,
END OF cs_po_type.


DATA: gt_po_header TYPE TABLE OF ty_po_header,
Gs_po_header TYPE ty_po_header.
DATA: gf_error(1) TYPE c,
Gv_datum TYPE sy-datum.
常量
常量组
如果程序中定义了一组相关的常量(如下例),必须要要注意常量的命名。
Example:
Constants:
C_periodic_mr_reason like eablg-ablesgr value ‘01’,
C_check_mr_reason    like eablg-ablesgr value ‘10’.
(以上命名方式是不符合要求的)


常量组的命名方式必须采用以下两种方式之一:
定义为结构,结构中包含所有相关常量:
Example:
constants: begin of cx_mrr
periodic like eablg-ablesgr value ‘01’, “Periodic
check like eablg-ablesgr    value ‘10’, “Check
          End of cx_mrr.


这种常量的使用和普通的结构用法相同:
lwa_eablg-ablesgr = cx_mrr-periodic
 
使用相同的前缀:
Example:
constants:
c_mrr_periodic like eablg-ablesgr value ‘01’,
  c_mrr_check    like eablg-ablesgr value ‘10’.


constants:
c_fi_doc_inv(2)    type c value ‘IN’,
  c_fi_doc_secdep(2) type c value ‘SD’.
常量名称
常量命名时不应该使用常量值作为其名称的一部分,而应该使用常量值所代表的业务含义或者是其在程序中的用途。
Unacceptable:
constants:
c_9(2)   type c value ‘9’,
c_one(1) type c value ‘1’.


Acceptable:
constants:
c_status_cleared(2) type c value ‘9’,
c_status_active(1)  type c value ‘1’.
常量声明
所有的常量均应定义为程序中的全局变量,不允许在子程序中定义局部常量。
全局变量
最小化全局变量的使用
为了使程序代码模块化,降低各模块之间的耦合度,必须尽量减少程序中使用的全局变量,如无必要尽量使用局部变量。
以下类型的变量可以定义为全局变量:
内表(跨子程序使用)
计数器
标识
常量(必须定义为全局)
结构
范围变量
类型定义(必须定义为全局)


以下类型的变量不能定义为全局变量:
工作区(如果需要,工作区应该定义为子程序参数进行传递)
变量组定义为结构
如果需要使用一组相关的全局变量,建议定义成一个全局的结构,而不是分别定义每个变量。


Unacceptable:
data: g_cnt_billdocs   type i,
  g_cnt_invoices   type i,
      g_cnt_contracts  type i.
 
Acceptable:
types: begin of ty_cnt,
billdocs   type i, “Number of billing documents
  invoices   type i, “Number of unsimulated invoices
      contracts  type i. “Number of related contracts
       end of ty_cnt.
...
data: x_cnt type ty_cnt. “Structure to keep track of counters
使用包含(include)
如果程序中定义全局变量的代码超过100行(包括全局变量、类型、常量以及相关的注释),必须单独建立一个include,将所有的变量声明语句从主程序中剥离出来,放到该包含程序中。
内表/结构
内表定义
使用类型声明内表
内表的定义必须使用表类型。


Unacceptable:
data: begin of i_mrtab occurs 0,
ablbelnr  like eabl-ablbelnr,
adatsoll   like eabl-adatsoll,
ablstat  like eabl-ablstat,
    end of i_mrtab.
Acceptable:
types: begin of ty_mrtab,
ablbelnr like eabl-ablbelnr, “MR Doc number
adatsoll like eabl-adatsoll, “Schedule MR Date
ablstat  like eabl-ablstat, “MR Status
     end of ty_mrtab.
types: ty_tab_mrtab type ty_mrtab occurs 0.
...
data: i_mrtab type ty_tab_mrtab.


对齐与注释
结构类型中定义的字段必须要按以下格式对齐;如果使用标准表字段,则字段名应该和标准表字段一致;结构类型中的字段后都应该有相应的文本注释。


  Unacceptable:
types: begin of ty_mrtab,
mrdoc like eabl-ablbelnr,
date like eabl-adatsoll,
ablstat  like eabl-ablstat,
     end of ty_mrtab.

Acceptable:
(注意:字段对齐,字段名称与表字段一致,字段后应该有文本注释)
types: begin of ty_mrtab,
ablbelnr like eabl-ablbelnr, “MR Doc number
adatsoll like eabl-adatsoll, “Schedule MR Date
ablstat  like eabl-ablstat, “MR Status
   end of ty_mrtab.


抬头行(Header Line)
定义内表时不能使用with header line定义其默认工作区,工作区必须在各个子程序中单独定义为局部变量。


Unacceptable:
data: i_mrtab type ty_tab_mrtab with header line.

Acceptable:
Form f_process_mr_data.
data: lwa_mr type ty_mrtab. “work area for i_mrtab


loop at i_mrtab into lwa_mr.
...
endloop.
Endform.
工作区
工作区不能定义为全局变量而应该在各个子程序中定义为局部变量使用。如果必须在子程序间传递工作区内容,则应该为相关子程序定义形式参数来实现。
Form f_process_mr_data.
data: lwa_mr type ty_mrtab. “work area for i_mrtab


loop at i_mrtab into lwa_mr.
...
perform f_validate_mr using lwa_mr.
...
endloop.
Endform.


文本变量
所有文本均应定位为文本元素(text element);如果不是必须,不允许在程序中使用文本硬代码或者将文本定义为常量来使用。


Unacceptable:
write: ‘Purchase’.


Acceptable:
write: text-c01. “Purchase


定位文本元素时应该使用合适的前缀;以下作为参考,可根据实际需要进行扩展。
Prefix
Description
Example
H
Header text
text-H01
C
Column heading
text-C01
E
Error text 
text-E08
W
Warning text
text-W03
I
Information text
text-I19
S
Summary text
text-S02






常用语句格式
Message
Message语句应该使用以下格式:
Message <message type>(<message class>) with <variable 1> <variable 2> etc.


Example:
message e304(ZCS01) with w_data-anlage w_data-device.
Call function
函数调用语句中,如果有不使用的参数,请将这些行删除而不要将这些行注释在代码中。同时,请注意所有的参数必须在代码中对齐。


Unacceptable:
    CALL FUNCTION 'BAPI_IDENTIFICATIONDETAILS_GET'
      EXPORTING
        BUSINESSPARTNER         = W_EBILL_CA-GPART
      TABLES
*        IDENTIFICATIONDETAIL  = I_BAPIBUS1006_ID_DETAILS
        RETURN               = I_BAPIRET2.
Acceptable:
    CALL FUNCTION 'BAPI_IDENTIFICATIONDETAILS_GET'
      EXPORTING
        BUSINESSPARTNER       = W_EBILL_CA-GPART
      TABLES
        RETURN               = I_BAPIRET2.
(请注意,这里的所有参数都工整地对齐)
Perform
使用perform语句调用子程序时,参数必须要对齐,且每一个参数单独一行,同时perform 语句及使用的参数必须有相应的注释。
Unacceptable:
perform f_delete_unused_bill_orders tables i_billorders using l_date.
Acceptable:
* Use the standard function to delete unused billing orders from the list 
*   in I_BILLORDERS
perform f_delete_unused_bill_orders tables i_billorders   “Bill order list
      using l_date. “Input date
Select
Select 语句结构
Select语句必须使用以下示例中的结构:


Example 1:
SELECT logiknr   "logical device no.
         equnr     "equipment number
         bis       "to date
    INTO wa_dev
    FROM egerh
   WHERE equnr EQ l_equip_no     "equipment number
     AND bis   GE p_rundate      "date ranges are in
     AND ab    LE p_rundate.     "todays date.


每一个字段必须分行列出,且对齐
每一个选取的字段必须有注释
“AND” 关键字必须分行列出,且对齐
 “EQ”, “GE” 等操作符必须对齐
不要使用“<>”, “=” 等操作符, 而应该使用 “NE”, “EQ”, “GT’, “LT” 等相同含义的操作符


Example2:
SELECT ekpo~ebeln  "po number
         ekpo~ebelp  "po item
         ekbe~belnr  "invoice
    INTO TABLE gt_ekbe
    FROM ekko
   INNER JOIN ekpo
      ON ekko~ebeln EQ ekpo~ebeln
   INNER JOIN ekbe
      ON ekpo~ebeln EQ ekbe~ebeln
     AND ekpo~ebelp EQ ekbe~ebelp
   WHERE ekko~lifnr EQ iv_lifnr               "Vendor Number
     AND ( ekko~bsart EQ cs_po_type-normal OR "PO Normal type
           ekko~bsart EQ cs_po_type-special ) "PO Special type
     AND ekbe~vgabe EQ cv_vgabe_invoice.      "invoice type


一般情况下不要使用表的别名,除非某个表在select语句中使用了两次以上
Inner joins 必须按以上格式对齐
Select语句中的关键字必须按以上格式对齐
语句中的复杂条件语句必须按以上格式书写


Select语句返回值
每一个select语句后面必须有相应的语句检查状态变量SY-SUBRC。如果确实不需要检查,那么必须有相应的注释说明原因。
IF
IF语句的结构和对齐方式如下例:
Example:
  IF w_tab-field1 EQ l_field1 AND
     w_tab-field2 EQ l_field2 OR
     ( w_tab-field3 LE l_field3 AND
       w_tab-field4 GE l_field4 ).
    ...
  ENDIF.
(请注意语句中变量、操作符以及AND/OR的对齐方式)


CHECK
CHECK语句的结构和对齐方式如下例:
Example:
CHECK w_tab1 IS NOT INITIAL AND
        w_tab2 IS NOT INITIAL.


在可能的情况下,尽量使用CHECK语句从而避免IF语句的多层嵌套:
Example:
Unacceptable
  LOOP AT gt_itab INTO ls_itab.
    IF a EQ b.
      IF c EQ d.
        IF e EQ f.
          ......
        ENDIF.
      ELSE.
        ......
      ENDIF.
    ELSE.
      CONTINUE.
    ENDIF.
  ENDLOOP.
Acceptable
  LOOP AT gt_itab INTO ls_itab.
    CHECK a EQ b.


    IF c EQ d.
      IF e EQ f.
        ......
      ENDIF.
    ELSE.
      ......
    ENDIF.
  ENDLOOP.


LOOP
LOOP语句的结构和对齐方式如下例:


Example:


  LOOP AT i_tab INTO w_tab WHERE value LE l_field
                             AND flag  EQ space.
    ...
  ENDLOOP.
(请注意以上变量和关键字的对齐方式)


READ
READ语句的结构和对齐方式如下例:


Example:
  READ TABLE i_tab WITH KEY key1 = l_key1_value
                            key2 = l_key2_value
                   TRANSPORTING NO FIELDS
                   BINARY SEARCH.
(请注意以上变量和关键字的对齐方式)


WHILE
WHILE语句的结构和对齐方式如下例:


Example:


  WHILE l_index1 LE l_index2 
    AND l_index3 GE l_index4.
    ...
  ENDWHILE.
(请注意以上变量和关键字的对齐方式)


DO
DO语句的结构和对齐方式如下例:
Example:


do l_count times
...
enddo.


CASE
CASE语句的结构和对齐方式如下例:
Example:


case wa_discon-status.
when c_discstat_complete.
...
when c_discstat_inprogress.
...
when c_discstat_start or c_discstat_onhold.
...
endcase.


如果IF..ELSE语句中比较的变量相同,那么应该始终使用CASE语句而不是IF语句。在每个WHEN代码区域中,不允许包含复杂的业务逻辑处理,而应该将这些业务逻辑的处理封装到子程序中,通过perform 语句调用。


Unacceptable


case wa_discon-status.
when c_discstat_complete.
loop at i_disconn into w_disc.
if w_disc-ab < g_sysdate.
...
endif.
read table i_tab with key field = w_disc-vkont.
...
endloop.
when ...
...
endcase.


Acceptable


case wa_discon-status.
when c_discstat_complete.
perform f_check_disconn_data.
when ...
...
endcase.


form f_check_disconn_data.
loop at i_disconn into w_disc.
if w_disc-ab < g_sysdate.
...
endif.
read table i_tab with key field = w_disc-vkont.
...
endloop.
endform.
CONCATENATE
CONCATENATE语句的结构和对齐方式如下例:


Example:
  CONCATENATE l_title       "bp salutation (mr./mrs. etc)
              l_firstname   "bp first name
              l_lastname    "bp last name
         INTO l_output_field
    SEPARATED BY l_separator.
(请注意以上变量和关键字的对齐方式,以及相应的注释)


报表程序规范
程序结构
所有可执行程序应该具有以下类似结构:
*&---------------------------------------------------------------------*
*& Report
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
*& Responsibility
*&---------------------------------------------------------------------*
* Program Name:
* Date written:
* Author's name:
* Last update:
* Program title:
* Project Name:
* Version:


*&---------------------------------------------------------------------*
* Description: (Incl. Related Function Area and System)
*&---------------------------------------------------------------------*
*


*&---------------------------------------------------------------------*
* Change History
*&---------------------------------------------------------------------*
*     Date   |   Programmer   |   Corr. #   |   Description
*            |                |             |
*            |                |             |
*&---------------------------------------------------------------------*


REPORT  MESSAGE-ID xx NO STANDARD PAGE HEADING LINE-SIZE 150.
TABLES:


*&---------------------------------------------------------------------*
* TYPES
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
* CONSTANTS
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
* GLOBAL DATA
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
* GLOBAL INTERNAL TABLES
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
* GLOBAL RANGES
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
* SELECTION-SCREEN
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
* GLOBAL MACROS
*&---------------------------------------------------------------------*


*&---------------------------------------------------------------------*
* EVENTS BEFORE MAIN PROGRAM
*&---------------------------------------------------------------------*
*INITIALIZATION.


*AT SELECTION-SCREEN OUTPUT.


*AT SELECTION-SCREEN ON VALUE-REQUEST FOR.


*AT SELECTION-SCREEN.


*TOP-OF-PAGE.


*&---------------------------------------------------------------------*
*
*        MAIN PROGRAM
*
*----------------------------------------------------------------------*
START-OF-SELECTION.
* Initialize data
  PERFORM frm_initialize_data.


* Process input data (for inbound interfaces)
  PERFORM frm_process_input_data.


* Select data from database
  PERFORM frm_select_data.


* Process selected data
  PERFORM frm_process_data.


END-OF-SELECTION.
* Combine results from the processing (may not be necessary)
  PERFORM frm_combine_data.


* Output result to file or spool/screen
  PERFORM frm_output_data.


程序框架应该符合以下的层次结构,也就是说每个子程序应该把其中的逻辑打散到更小的子程序中,以使子程序的逻辑简单化,增加代码的可重用性和可靠性。


Example:
典型的程序层次结构如下:


|__perform f_select_data
| |
| |__ perform f_select_device_data.
| | |
| | |__ perform f_select_equi
| | |
| | |__ perform f_select_egerh
| | |
| | |__ perform f_select_egers
| |
| |__ perform f_select_inst_data.
|
|__ perform f_process_data
| |
| |__ perform f_process_mru_data.
| |
| |__ perform f_process_device_data.
|
|__ perform f_output_data.


数据初始化子程序
数据初始化子程序主要用于数据读取、处理所需的初始化操作,比如初始化range变量、系统变量、全局变量等。如果需要对多个range变量进行初始化,那么需要将它们分别放到各自的子程序中处理。


Example:
form f_initialize_data.


* Initialize system fields
perform f_init_data_sysfields.


* Initialize ranges
perform f_init_data_ranges.
endform f_initialize_data.


form f_init_data_sysfields.
g_sysdate = sy-datum.
g_sysuname = sy-uname.
endform


form f_init_data_ranges.
r_divisions    = c_range_search_eq.   “c_range_search_eq = ‘IEQ’
r_division-low = c_div_elec.
append r_division.
endform


 数据存取子程序
此子程序作为数据存取的唯一入口,通过perform来调用各个select子程序。所有的数据存取语句(select)都应该包含在相应的select子程序中;通常情况下,一个select语句应该包含在一个独立的子程序中。


Example:
form f_select_data.
* Get MRUs
perform f_select_mru.


* Get Installations.
perform f_select_inst.
Endform.


form f_select_mru.


* Get all MRUs
Select ableinh
Into i_mrus
From te422
Where adatsoll in s_adatsoll.
endform.


form f_select_inst.

Check i_mrus[] is not initial.


* Get all installations
Select anlage
Into i_insts
From eanlh
For all entries in i_mrus
Where ableinh = i_mrus-anlage.
endform.


数据处理子程序
该子程序应该是处理数据的唯一入口,所有逻辑处理的子程序应该通过该子程序来调用。


Example:
form f_process_data.
* Process MRUs
perform f_process_mru.


* Process Installation Data
perform f_process_inst.
Endform.


form f_process_mru.
* Process MRUs that start with ‘M*’
perform f_process_mru_main.


* Process All other MRUs that don’t start with ‘M*’
perform f_process_mrus_sub.
endform.


form f_process_inst.
...
endform.


数据组合子程序
该子程序仅用于将F_PROCESS*处理的结果组合成最终的输出结构。 
该子程序不是必需的。


Example:
form f_combine_data.


Loop at i_inst into w_inst.
Read table i_easts with key anlage = w_inst-anlage
Binary search.
....


w_output-anlage = w_inst-anlage.
w_output-device = w_easts-device.
Append w_output to i_output.
Endloop.
Endform.


数据输出子程序
该子程序仅用于输出结果。所有相关的数据处理逻辑不应该出现在该子程序中,而应该出现在F_PROCESS* 子程序中。该子程序的作用只是用于格式化输出。


Example:
form f_output_data.
* Output data
perform f_write_column_heading.


format color col_red.
Loop at i_output into w_output.
write at: /g_col1 w_output-anlage “installation
   g_col2 w_output-device. “device
Endloop.
form color off.
Endform.
子程序规范
子程序是代码模块化的非常好的一种方法。
模块化代码
使用子程序的主要目的是使程序代码模块化。程序中使用的公共代码应该转换为子程序,而不是在每个地方使用代码拷贝。 
Example:
Assume the following declarations
types: begin of ty_devmr,
equnr    like eabl-equnr, “Equipment number
adatsoll like eabl-adatsoll, “Schedule MR Date
flag     type c, “Indicate old MR or not
       end of ty_devmr.


types: ty_tab_devmr type ty_devmr occurs 0.


data: i_devmr1 type ty_tabmr.
data: i_devmr2 type ty_tabmr.


Unacceptable:


Form f_check_device_lists
...
loop at i_devmr1 assigning <fs_mr> where adatsoll < g_sysdate.
<fs_mr>-flag = c_true.
endloop.


loop at i_devmr2 assigning <fs_mr> where adatsoll < g_sysdate.
<fs_mr>-flag = c_true.
endloop.
...
Endform.


Acceptable:


Form f_check_device_lists
...
perform f_check_single_dev_list tables i_devmr1.
perform f_check_single_dev_list tables i_devmr2.
...
Endform.


Form f_check_single_dev_list tables pi_devlist type ty_tab_devmr.
* Loop at the list of input devices and set the flag.
loop at pi_devlist assigning <fs_mr> where adatsoll < g_sysdate.
<fs_mr>-flag = c_true.
endloop.
Endform.
参数
参数命名
子程序参数命名参考2.2.2变量命名规范。
参数类型
所有传入子程序的参数都必须指定参数类型。


Unacceptable:
Form f_process_billdoc using pwa_billdoc.
...
Endform.


Acceptable:
Form f_process_billdoc using pwa_billdoc type ty_billdoc.
...
Endform.
Table参数
通常情况下,数据量大的内表应该定义为全局变量而不应该通过参数来传递。而对于数据量小的内表,有时候则是通过子程序参数来传递(程序员应该在这一点上合理把握)。参数类型通常情况下使用type而不是structure来定义。


Unacceptable:
Form f_process_billdoc tables pi_docs structure i_docs.
...
Endform.


Acceptable:
Form f_process_billdoc tables pi_docs type ty_tab_doclist.
...
Endform.
参数个数要求
当有大量的有关联的简单变量需要传入时,最好是采用以下方式之一:
创建一个公用的结构,以结构的方式传入参数
通过小的内表来传入值
使用range变量来传入值
程序员应该合理判断采用哪种方式更合适。


Unacceptable:


...
perform f_process_date using l_date1
    l_date2
    l_datecheck_a
    l_date3
    l_date4
     l_datecheck_b.
...


Form f_process_date using l_startdate_a like sy-datum
 l_enddate_a   like sy-datum
 l_datecheck_a type c
 l_startdate_b like sy-datum
       l_enddate_b   like sy-datum
 l_datecheck_b type c.
...
if  l_startdate_a < sy-datum
and l_enddate_a   > sy-datum
and l_datecheck_a = c_true
and l_startdate_b < sy-datum
and l_enddate_b > sy-datum
and l_datecheck_b = c_true.
...
Endif.
... 
Endform.


Acceptable:


...
types: begin of ty_date,
start like sy-datum, “Start Date
end like sy-dateum,    “End Date
check type c, “Date check flag
 end of ty_date.
...


* lwa_date1 and lwa_date2 are of type ty_date and contain the
* the required dates.
perform f_process_date using lwa_date1
        lwa_date2.


...

Form f_process_date using  pwa_date1 type ty_date
pwa_date2 type ty_date.

if  pwa_date1-start < sy-datum
and pwa_date1-end   > sy-datum
and pwa_date1-check = c_true
and pwa_date2-start < sy-datum
and pwa_date2-end   > sy-datum
and pwa_date2-check = c-true.
...
Endif.


Endform.
参数对齐
子程序使用的形式变量必须按照以下格式对齐。


Unacceptable:


Form f_process_date tables pi_tab type ty_tab_tab
using l_date like sy-datum
 l_datecheck type c.
... 
Endform.


Acceptable:


Form f_process_date tables pi_tab      type ty_tab_tab 
 using l_date      like sy-datum
          l_datecheck type c.
... 
Endform.
子程序结构
每一个子程序应该只包含一个逻辑功能块,如果需要多个逻辑块,那么该子程序应该拆分为多个子程序。


每一个select语句应该有一个单独的子程序(关系非常紧密的多个select语句可以放到一个子程序中)
如果子程序中包含两个以上的连续loop语句,那么应该把它们拆分到不同的子程序中
如果一个子程序中包含的代码行超过100,那么应该把该子程序拆分成多个子程序
Unacceptable:


Form f_process_data.


Loop at i_movein into w_movein.
...
Endloop.


Loop at i_inst into w_inst.
... 
Endloop.
Endform.


Acceptable:
Form f_process_data.
* Process the move-in records
Perform f_process_movein.


* Process the installation records
Perform f_process_insts.
Endform.



Form f_process_movein.
Loop at i_movein into w_movein.
...
Endloop.
Endform.


Form f_process_inst.
Loop at i_inst into w_inst.
... 
Endloop.
Endform.


Perform语句
使用perform语句调用子程序时,传递的参数必须对齐且注释;每一个参数应该分行列出。
Unacceptable:


perform f_delete_unused_bill_orders tables i_billorders using l_date.


Acceptable:


* Use the standard function to delete unused billing orders from the list 
*   in I_BILLORDERS
perform f_delete_unused_bill_orders tables i_billorders   “Bill order list
      using  l_date.     “Input date


子程序注释
所有的子程序必须包含注释以说明子程序的功能以及输入输出参数的含义。


Example:
*&---------------------------------------------------------------------*
*&      Form  F_CONV_DATE
*----------------------------------------------------------------------*
*  This is used to convert the date in the yyyymmdd format into the    *
*  the text string: dd.mm.yyyy format.        *
*----------------------------------------------------------------------*
*  -->  p_inp_date = The original date in the yyyymmdd format  *
*  <--  p_out_date = The date in the dd.mm.yyyy format.  *
*----------------------------------------------------------------------*
form f_conv_dat using p_inp_date     like sy-datum
          p_out_date(10) type c.
Endform.




功能模块规范
函数模块结构
函数模块的主要代码区域(FUNCTION和ENDFUNCTION之间) 不能包含复杂的逻辑:  
只能包含子程序或其他函数的调用语句
可以包含简单的 IF, CASE, LOOP 等语句
所有的复杂的逻辑处理(包含select)必须定义为子程序中,放入相应的函数模块包含程序中
如果函数模块中全部的代码(包含注释文本)不超过100行,那么可以例外
函数返回值
函数调用后必须检查相应的返回状态变量SY-SUBRC,如果确实不需要检查,应该说明原因。


函数调用
调用函数模块时,如果有不使用的IMPORTING/EXPORTING/TABLE参数,请删除相应的注释行。
同时,所有的参数必须格式对齐。


Unacceptable:


    CALL FUNCTION 'BAPI_IDENTIFICATIONDETAILS_GET'
      EXPORTING
        BUSINESSPARTNER         = W_EBILL_CA-GPART
      TABLES
*        IDENTIFICATIONDETAIL  = I_BAPIBUS1006_ID_DETAILS
        RETURN               = I_BAPIRET2.


Acceptable:


    CALL FUNCTION 'BAPI_IDENTIFICATIONDETAILS_GET'
      EXPORTING
        BUSINESSPARTNER       = W_EBILL_CA-GPART
      TABLES
        RETURN                = I_BAPIRET2.
(Please note, that all the fields are aligned neatly)




屏幕对话程序规范
屏幕逻辑流结构
所有自定义的主屏幕逻辑流均需定义为以下类似结构;子屏幕不应包含:TOOLBAR、 USER-COMMAND和EXIT-COMMAND几个部分 
PROCESS BEFORE OUTPUT.
* Set toolbar and titlebar
MODULE status_xxxx.  “xxxx-screen number


* Set output attribute for screen fields
MODULE dynpro_fields_xxxx.  “xxxx-screen number


* move data from program to screen fields
MODULE dynpro_data_out_xxxx.  “xxxx-screen number


* Table control loop
LOOP AT gt_tab INTO gs_tab WITH CONTROL tc_xxxx CURSOR tc_xxxx-current_line.  
*  Set output attribute of table control fields
 MODULE dynpro_fields_tc_xxxx.


*  move data from program to table control fields
 MODULE dynpro_data_out_tc_xxxx. 
ENDLOOP. 


PROCESS AFTER INPUT.
* Process EXIT-COMMAND
MODULE exit_command_xxxx AT EXIT-COMMAND.  “xxxx-screen number


* check input validation of screen fields 
CHAIN.
            FIELD: a,
                    b.
 MODULE dynpro_data_check_<w>_xxxx ON CHAIN-REQUEST.  “<w>-meaning words
ENDCHAIN.


* move data from screen fields to program
CHAIN.
            FIELD: a,
                    b,
c.
 MODULE dynpro_data_in_xxxx ON CHAIN-REQUEST.  “xxxx-screen number
ENDCHAIN.


* Table control loop
LOOP AT gt_tab.  
*  check input validation of table control fields 
 CHAIN.
              FIELD: c,
                     d.
   MODULE dynpro_data_check_<w>_tc_xxxx ON CHAIN-REQUEST.“<w>-meaning words 
 ENDCHAIN.


*  move data from table control fields to program
 CHAIN.
              FIELD: c,
                     d,
e.
   MODULE dynpro_data_in_tc_xxxx ON CHAIN-REQUEST.
 ENDCHAIN.
ENDLOOP. 


* Process USER-COMMAND
MODULE user_command_xxxx.  “xxxx-screen number


PROCESS ON VALUE-REQUEST.
* Process f4 for screen field a
FIELD a MODULE dynpro_f4_a_xxxx.  “xxxx-screen number


PAI/PBO/POV 模块
所有屏幕模块必须包含以下类似结构
MODULE <module_name> [INPUT|OUTPUT].
* Call subroutine of each special module
PERFORM f_<module_name>_[pai|pbo|pov].
ENDMODULE.


在〔MODULE…..ENDMODULE〕中不允许有任何逻辑处理语句,更不能定义任何类型的变量,只允许使用PERFORM语句调用特定的子程序。所有相关的业务逻辑只能包含在该子程序或其调用的下层子程序中. 
Example:


Unacceptable:


MODULE dynpro_data_out_0100 OUTPUT.
ekko-ebeln = gs_ekko-ebeln.
          ekko-lifnr = gs_ekko-lifnr.
          SELECT SINGLE name1 INTO dynpro_name1 FROM lfa1 WHERE lifnr = ekko-lifnr.
ENDMODULE.


Acceptable:


MODULE dynpro_data_out_0100 OUTPUT.
PERFORM f_dynpro_data_out_0100_pbo.
ENDMODULE.


FORM f_dynpro_data_out_0100_pbo.
ekko-ebeln = gs_ekko-ebeln.
          ekko-lifnr = gs_ekko-lifnr.
          SELECT SINGLE name1 INTO dynpro_name1 FROM lfa1 WHERE lifnr = ekko-lifnr.
ENDFORM.
User-command模块
这个模块只能用于处理除EXIT-COMMAND以外的所有用户按钮操作; 在该模块调用的子程序中,只允许使用CASE判断屏幕触发的按钮事件,任何业务逻辑语句均需要包含在相应的子程序中。相应子程序应按照以下规则命名:
Frm_fcode_<okcode>_<dynnr>
<okcode>-按钮名称
<dynnr>-屏幕号
所有屏幕事件代码均使用全局变量ok_code接收,同时必须定义全局变量ok_save,在user_command模块中按照以下方式来处理
EXAMPLE:


form f_user_command_0100_pai.
OK_SAVE = OK_CODE. 
CLEAR OK_CODE.


CASE OK_SAVE.
WHEN ‘ADD’.  “add data line
perform f_fcode_add_0100.


WHEN ‘SAVE’. “save data
perform f_fcode_save_0100.


  WHEN OTHERS.
ENDCASE.
Endform.
BADI增强规范
实施BADI增强需按照以下步骤实现:


Step
Description
1
在SE19中创建客BADI实施:命名参考本文档“1.5 BADI实施命名规范”
2
在相应的BADI实施类中,增加自定义方法,所有业务逻辑的处理均应包含在自定义方法中:Z<XX>_<BADI_METH>_<nn>;
3
在BADI实施标准方法中使用call method调用相应的自定义业务处理方法
注意:在BADI实施模块的标准方法中,不能有复杂的业务逻辑


Example:


Unacceptable:


METHOD if_ex_ac_document~change_initial.


     LOOP AT im_document-item INTO w_item.


      w_ebeln = w_item-ebeln.
      w_ebelp = w_item-ebelp.


      CHECK w_ebeln IS NOT INITIAL AND w_ebelp IS NOT INITIAL.




      SELECT SINGLE * FROM ekpo
 WHERE ebeln EQ w_ebeln
 AND ebelp EQ w_ebelp.


......


    ENDLOOP.




ENDMETHOD.                    "if_ex_ac_document~change_initial


Acceptable:


METHOD if_ex_ac_document~change_initial.
* Change document data
CALL METHOD ZSG1_CHANGE_INITIAL_01
EXPORTING 
IM_DOCUMENT = IM_DOCUMENT
IMPORTING
 EX_DOCUMENT = EX_DOCUMENT.


ENDMETHOD.                    "if_ex_ac_document~change_initial


METHOD ZSG1_CHANGE_INITIAL_01
* Get data from purchasing order
CALL METHOD ZFILL_DOC_ITEM_WITH_PO_ITEM.
EXPORTING 
IM_DOCUMENT = IM_DOCUMENT
IMPORTING
 EX_DOCUMENT = EX_DOCUMENT.


* Other enhancement data
CALL METHOD ZFILL_DOC_ITEM_WITH_CUSTOM_DATA
EXPORTING 
IM_DOCUMENT = IM_DOCUMENT
IMPORTING
 EX_DOCUMENT = EX_DOCUMENT.


ENDMETHOD.                    "ZSG1_CHANGE_INITIAL_01


METHOD ZFILL_DOC_ITEM_WITH_PO_ITEM
* Get data from purchasing order
LOOP AT im_document-item INTO w_item.


      w_ebeln = w_item-ebeln.
      w_ebelp = w_item-ebelp.


      CHECK w_ebeln IS NOT INITIAL AND w_ebelp IS NOT INITIAL.




      SELECT SINGLE * FROM ekpo
 WHERE ebeln EQ w_ebeln
 AND ebelp EQ w_ebelp.


......


   ENDLOOP.
ENDMETHOD.                    "ZFILL_DOC_ITEM_WITH_PO_ITEM.


CMOD增强规范
CMOD中的增强用户出口包含一个函数模块 (EXIT_*) 和一个可以客户化修改的include程序。该include程序中不允许包含任何逻辑!
为了实施一个增强,应该将用户出口函数模块 (EXIT_*) 拷贝为一个用户函数模块(按命名规范命名z*),所有的逻辑处理应该包含在这个拷贝出的函数模块中;而出口inlucde程序,仅需要调用该函数的语句。


例如,假定有一个用户出口 EXIT_SAPLES55_001,它包含一个inlcude程序ZXSAPL02;那么应该按照以下步骤来实施这个增强出口:


Step
Description
1
在CMOD中创建一个项目,包含所需的增强
2
拷贝用户出口函数模块EXIT_SAPLES55_001为一个用户函数 模块 (例如,新的函数模块名为ZISCS_EXITES55_COBJ)
3
在函数模块ZISCS_EXITES55_COBJ中加入所需的增强逻辑
4
在inlude文件ZXSAPL02中调用函数模块ZISCS_EXITES55_COBJ,并传递函数模块EXIT_SAPLES55_001的所有参数


如果需要在用户出口中增加一块全新的逻辑,那么应该拷贝出新的函数模块来实现。


BTE增强规范
所有BTE的开发均必须在客户定制范围内实现,不允许对SAP标准应用的BTE做任何更改;实施BTE增强需按照以下步骤实现:


Step
Description
1
在FIBF中创建客户定制产品PROD:Z<XX>_<nnn>,如:ZFIN_001
2
拷贝相应BTE函数模版到自定义函数模块:<PROD>_<event>_<desc>,如:ZFIN_001_1030_FIDOC_SAVE
3
在函数模块ZFIN_001_1030_FIDOC_SAVE中增加相应的逻辑,函数中的代码必须符合本文档中“功能模块规范“要求
4
在FIBF相应P/S Module或Process Module中客户产品部分指定:事件、产品、函数模块,如:00001030 | ZFIN_001 | ZFIN_001_1030_FIDOC_SAVE


程序性能相关代码规范
SQL语句
始终把条件放在where子句中而不要使用check语句来检查,这样数据库系统可以使用索引(如果可能),同时这样可以大大降低网络负载。
Example:


Unacceptable:
SELECT * FROM SBOOK INTO SBOOK_WA.
  CHECK: SBOOK_WA-CARRID = 'LH' AND
         SBOOK_WA-CONNID = '0400'.
ENDSELECT.


acceptable:
SELECT * FROM SBOOK INTO SBOOK_WA
  WHERE CARRID = 'LH' AND
        CONNID = '0400'.
ENDSELECT.


如果需要检查表或视图是否有满足某条件的记录存在,请使用Select ... Up To 1 Rows 语句,而不要使用带Exit的Select-Endselect-loop语句。如果条件中包含所有的主键字段,则应该使用Select Single语句。Select Single语句仅需要与数据库通信一次,而 Select-Endselect则需要两次。
Example:


Unacceptable:
SELECT * FROM SBOOK INTO SBOOK_WA
    WHERE CARRID = 'LH'.
  EXIT.
ENDSELECT.


acceptable:
SELECT * FROM SBOOK INTO SBOOK_WA
  UP TO 1 ROWS
  WHERE CARRID = 'LH'.
ENDSELECT.


如果想查询最大值、最小值、总和、平均值或者条目数,则应该使用统计函数;这样可以大大降低网络负载。
Example:


Unacceptable:
DATA: MAX_MSGNR type t100-msgnr.
MAX_MSGNR = '000'.
SELECT * FROM T100 INTO T100_WA
  WHERE SPRSL = 'D' AND
        ARBGB = '00'.
  CHECK: T100_WA-MSGNR > MAX_MSGNR.
  MAX_MSGNR = T100_WA-MSGNR.
ENDSELECT.


acceptable:
DATA: MAX_MSGNR type t100-msgnr.
SELECT MAX( MSGNR ) FROM T100 INTO max_msgnr
  WHERE SPRSL = 'D' AND
        ARBGB = '00'.


如果仅需读取表或视图中的个别字段,那么应该使用select列表而不要使用Select * ;这样可以大大降低网络负载。
Example:


Unacceptable:
SELECT * FROM DD01L INTO DD01L_WA
  WHERE DOMNAME LIKE 'CHAR%'
        AND AS4LOCAL = 'A'.
ENDSELECT.
acceptable:
SELECT DOMNAME FROM DD01L
  INTO DD01L_WA-DOMNAME
  WHERE DOMNAME LIKE 'CHAR%'
        AND AS4LOCAL = 'A'.
ENDSELECT.


更新数据库时,尽可能使用列更新而不要使用单行更新。这样可以大大降低网络负载。
Example:


Unacceptable:
SELECT * FROM SFLIGHT INTO SFLIGHT_WA.
  SFLIGHT_WA-SEATSOCC =
    SFLIGHT_WA-SEATSOCC - 1.
  UPDATE SFLIGHT FROM SFLIGHT_WA.
ENDSELECT.


acceptable:
UPDATE SFLIGHT
  SET SEATSOCC = SEATSOCC - 1.


对于经常使用的select语句,尽量使用索引。在select语句的where子句中,尽量使用通过AND连接的索引字段,这样数据库总是会使用索引。注意:过于复杂的where子句对于数据库系统语句优化器来说都是不利于处理的。
Example:


Unacceptable:
SELECT * FROM SBOOK CLIENT SPECIFIED INTO SBOOK_WA
  WHERE CARRID = 'LH'
    AND CONNID = '0400'.
ENDSELECT.


acceptable:
SELECT * FROM SBOOK CLIENT SPECIFIED INTO SBOOK_WA
  WHERE MANDT IN ( SELECT MANDT FROM T000 )
    AND CARRID = 'LH'
    AND CONNID = '0400'.
ENDSELECT.


对于经常使用的只读表,尽量使用SAP缓存。可以大大降低网络负载。
Example:


Unacceptable:
SELECT SINGLE * FROM T100 INTO T100_WA
  BYPASSING BUFFER
  WHERE     SPRSL = 'D'
        AND ARBGB = '00'
        AND MSGNR = '999'.


acceptable:
SELECT SINGLE * FROM T100  INTO T100_WA
  WHERE     SPRSL = 'D'
        AND ARBGB = '00'
        AND MSGNR = '999'.


一般情况下使用Into Table总是会快于Select循环中使用Append 语句
Example:


Unacceptable:
DATA T006_WA TYPE T006.
CLEAR X006.
SELECT * FROM T006 INTO T006_WA.
  APPEND T006_WA TO X006.
ENDSELECT.


acceptable:
SELECT * FROM T006 INTO TABLE X006.


更新数据库表时,尽可能使用数组操作而不要使用单行操作。应用程序和数据库系统频繁通信会造成大量的系统负载。
Example:


Unacceptable:
LOOP AT TAB INTO TAB_WA.
  INSERT INTO CUSTOMERS VALUES TAB_WA.
ENDLOOP.


acceptable:
INSERT CUSTOMERS FROM TABLE TAB.


如果选择的数据只处理一次,那么应该使用Select-Endselect循环,二不要使用Into Table把数据收集到内表中再处理;内表处理会占用更多的空间。
Example:


Unacceptable:
SELECT * FROM T006
  INTO TABLE X006.
LOOP AT X006 INTO X006_WA.
ENDLOOP.


acceptable:
SELECT * FROM T006 INTO X006_WA.
ENDSELECT.


如果要处理表连接,应该使用视图而不要使用嵌套的select语句;这样可以有效的降低网络负载。
Example:


Unacceptable:
SELECT * FROM DD01L INTO DD01L_WA
  WHERE DOMNAME LIKE 'CHAR%'
        AND AS4LOCAL = 'A'.
  SELECT SINGLE * FROM DD01T INTO DD01T_WA
    WHERE   DOMNAME    = DD01L_WA-DOMNAME
        AND AS4LOCAL   = 'A'
        AND AS4VERS    = DD01L_WA-AS4VERS
        AND DDLANGUAGE = SY-LANGU.
ENDSELECT.


acceptable:
SELECT * FROM DD01V INTO  DD01V_WA
  WHERE DOMNAME LIKE 'CHAR%'
        AND DDLANGUAGE = SY-LANGU.
ENDSELECT.


如果要从几个逻辑关联的表中读取数据,应该使用表连接而不要使用嵌套的select语句;这样可以有效的降低网络负载。
Example:


Unacceptable:
SELECT * FROM SPFLI INTO SPFLI_WA.
  SELECT * FROM SFLIGHT INTO SFLIGHT_WA
      WHERE CARRID = SPFLI_WA-CARRID
        AND CONNID = SPFLI_WA-CONNID.
  ENDSELECT.
ENDSELECT.


acceptable:
SELECT * INTO WA
    FROM SPFLI AS P INNER JOIN SFLIGHT AS F
      ON P~CARRID = F~CARRID AND
         P~CONNID = F~CONNID.
ENDSELECT.


通常情况下嵌套的Select 循环或者FOR ALL ENTRIES 都可以通过子查询来实现;子查询可以有效的降低网络负载。
Example:


Unacceptable:
SELECT * FROM SPFLI
  INTO TABLE T_SPFLI
  WHERE CITYFROM = 'FRANKFURT'
    AND CITYTO = 'NEW YORK'.
SELECT * FROM SFLIGHT AS F
    INTO SFLIGHT_WA
    FOR ALL ENTRIES IN T_SPFLI
    WHERE SEATSOCC < F~SEATSMAX
      AND CARRID = T_SPFLI-CARRID
      AND CONNID = T_SPFLI-CONNID
      AND FLDATE BETWEEN '19990101' AND '19990331'.
ENDSELECT.


acceptable:
SELECT * FROM SFLIGHT AS F INTO SFLIGHT_WA
    WHERE SEATSOCC < F~SEATSMAX
      AND EXISTS ( SELECT * FROM SPFLI
                     WHERE CARRID = F~CARRID
                       AND CONNID = F~CONNID
                       AND CITYFROM = 'FRANKFURT'
                       AND CITYTO = 'NEW YORK' )
      AND FLDATE BETWEEN '19990101' AND '19990331'.
ENDSELECT.


内表操作
如果内表的记录条数很多(>20),那么对全表进行线性查询是非常费时的。应该尽量使内表排序,并使用二分法搜索,或者使用SORTED TABLE。假如内表含有n条记录,线性搜索执行时间为O( n ) ,而二分法执行时间仅为O( log2( n ) )。
Example:


Unacceptable:
* Entries: 1000, Line width: 100
*                Key width:   20
* The READ ends with SY-SUBRC=4


READ TABLE ITAB INTO WA
                WITH KEY K = 'X'.


acceptable:
* Entries: 1000, Line width: 100
*                Key width:   20
* The READ ends with SY-SUBRC=4


READ TABLE ITAB INTO WA
                WITH KEY K = 'X'
                BINARY SEARCH.


如果需要使用不同的关键字重复读取内表,那么应该在代码中保存相应的次级索引。有了次级索引就可以使用二分法搜索加上一个索引读取来替代线性搜索。 
Example:


Unacceptable:
* Entries: 1000, Line width: 100
*                Key width:   20
* The READ locates the 500th entry.


READ TABLE ITAB INTO WA
     WITH KEY DATE = SY-DATUM.
IF SY-SUBRC = 0.
  " ...
ENDIF.


acceptable:
* Entries: 1000, Line width: 100
*                Key width:   20
* The READ locates the 500th entry.


READ TABLE SEC_IDX INTO  SEC_IDX_WA
     WITH KEY DATE = SY-DATUM
          BINARY SEARCH.
IF SY-SUBRC = 0.
  READ TABLE ITAB INTO WA
                  INDEX SEC_IDX_WA-INDX.
  " ...
ENDIF.


LOOP ... WHERE比LOOP/CHECK性能更好,因为LOOP ... WHERE由内部来判断指定的条件。同所有的逻辑表达式一样,如果操作数的类型相同,则性能会更好。 可能的情况下,使用FROM i1且(或者)TO i2可以进一步提高性能。
Example:


Unacceptable:
* Entries: 1000, Line width: 500
*                Key width:   20
* 5 entries of which match the key condition


LOOP AT ITAB INTO WA.
  CHECK WA-K = 'X'.
  " ...
ENDLOOP.


acceptable:
* Entries: 1000, Line width: 500
*                Key width:   20
* 5 entries of which match the key condition


LOOP AT ITAB INTO WA WHERE K = 'X'.
  " ...
ENDLOOP.


排序表中的记录通过二分法来定位,执行的时间依赖于表中记录数(O (log n))。而哈希表中记录通过哈希算法来定位,执行的时间始终是固定的(O (1)), 而与表的大小无关。 哈希表适用于单条记录的读取,而排序表则适用于符合部分条件的顺序的表循环操作。
Example:


Unacceptable:
* Entries: 1000
* Line width: 100, key width: 20
* STAB is a unique sorted table, 250 entries are read


DO 250 TIMES.
  N = 4 * SY-INDEX.
  READ TABLE STAB INTO WA WITH TABLE KEY K = N.
  IF SY-SUBRC = 0.
    " ...
  ENDIF.
ENDDO.


acceptable:
* Entries: 1000
* Line width: 100, key width: 20
* HTAB is a hased table, 250 entries are read


DO 250 TIMES.
  N = 4 * SY-INDEX.
  READ TABLE HTAB INTO WA WITH TABLE KEY K = N.
  IF SY-SUBRC = 0.
    " ...
  ENDIF.
ENDDO.


哈希表为单条记录读取而优化,记录之间没有特定的顺序关系。因此对哈希表进行部分键值的顺序读取,其性能是没有优化的,而是每一条记录都要检查是否符合条件(即全表搜索)。排序表则是按照主键进行升序排列的。如果查询条件是这样的格式"k1 = v1 AND ... AND kn = vn"(k1 .. kn与表主键的左边部分匹配),那么表的读取是被核心代码优化的,因此只会访问符合条件的记录,而不是全表搜索。
Example:


Unacceptable:
* Entries: 10000, Line width: 100
* key width: 60,  Subkey width: 20
* HTAB is a hashed table, 2000 entries are read
* Key fields: K, DATA


LOOP AT HTAB INTO WA WHERE K = SUBKEY.
  " ...
ENDLOOP.




acceptable:
* Entries: 10000, Line width: 100
* key width: 60,  Subkey width: 20
* STAB is a sorted table, 2000 entries are read
* Key fields: K, DATA


LOOP AT STAB INTO WA WHERE K = SUBKEY.
  " ...
ENDLOOP.


使用MODIFY语句更新内表时,使用" TRANSPORTING f1 f2 ..."可以提高执行的效率;特别是表行越长越明显。
Example:


Unacceptable:
* Line width: 500
* The complete line is moved.


WA-DATE = SY-DATUM.
MODIFY ITAB FROM WA INDEX 1.


acceptable:
* Line width: 500
* Only the 8 bytes of the selected
* component are moved.


WA-DATE = SY-DATUM.
MODIFY ITAB FROM WA INDEX 1 TRANSPORTING DATE.


使用"LOOP ... ASSIGNING ..."直接存取内表行,可以大大提高内表记录的更新速度。
Example:


Not preferred:
* Entries: 100 (outer table), 20 (inner table)
* The entries to be modified: 50
* Actually, only the component FLAG is updated.
* However, the complete lines are moved.


LOOP AT ITAB INTO WA.
  I = SY-TABIX MOD 2.
  IF I = 0.
    WA-FLAG = 'X'.
    MODIFY ITAB FROM WA.
  ENDIF.
ENDLOOP.


Preferred:
* Entries: 100 (outer table), 20 (inner table)
* Entries to be modified: 50
* The component FLAG is updated directly.


LOOP AT ITAB ASSIGNING <WA>.
  I = SY-TABIX MOD 2.
  IF I = 0.
    <WA>-FLAG = 'X'.
  ENDIF.
ENDLOOP.


使用从下到上的策略来填充内表,那么系统开销会根据数据结构的层次成倍的增加。因为每一个下层内表的数据都需要移动到它们的上层数据结构中。相反,使用从上到下的策略填充内表,先填充外层内表,然后使用"LOOP ... ASSIGNING"直接填充内层内表,这样内层内表的数据只需要移动一次就可以了。
Example:


Not preferred:
* Entries: 50 (outer table), 10 (inner tables)
* Line width: 500 (outer), 4 (inner)


DO 50 TIMES.
  CLEAR WA.
  DO 10 TIMES.
    APPEND N TO WA-INTTAB.
    ADD 1 TO N.
  ENDDO.
  APPEND WA TO ITAB.
ENDDO.


Preferred:
* Entries: 50 (outer table), 10 (inner tables)
* Line width: 500 (outer), 4 (inner)


DO 50 TIMES.
  APPEND INITIAL LINE TO ITAB.
ENDDO.
LOOP AT ITAB ASSIGNING <F>.
  DO 10 TIMES.
    APPEND N TO <F>-INTTAB.
    ADD 1 TO N.
  ENDDO.
ENDLOOP.


如果需要汇总表记录,那么请使用COLLECT语句 ! 
Example:


Unacceptable:
* TAB1: 1000 entries, Line width: 32
* 500 entries have different keys


LOOP AT ITAB1 INTO WA1.
  READ TABLE ITAB2 INTO WA2 WITH KEY K = WA1-K BINARY SEARCH.
  IF SY-SUBRC = 0.
    ADD: WA1-VAL1 TO WA2-VAL1,
         WA1-VAL2 TO WA2-VAL2.
    MODIFY ITAB2 FROM WA2 INDEX SY-TABIX TRANSPORTING VAL1 VAL2.
  ELSE.
    INSERT WA1 INTO ITAB2 INDEX SY-TABIX.
  ENDIF.
ENDLOOP.


acceptable:
* TAB1: 1000 entries, Line width: 32
* 500 entries have different keys


LOOP AT ITAB1 INTO WA.
  COLLECT WA INTO ITAB2.
ENDLOOP.
SORT ITAB2 BY K.


如果要将一个表的记录追加到另一个表中,请使用"APPEND LINES OF itab1 TO itab2" .
Example:


Unacceptable:
* Entries: 500 (ITAB1), 500 (ITAB2)
* Line width: 500
* ITAB1 is appended line by line to ITAB2.


LOOP AT ITAB1 INTO WA.
  APPEND WA TO ITAB2.
ENDLOOP.




acceptable:
* Entries: 500 (ITAB1), 500 (ITAB2)
* Line width: 500
* ITAB1 is appended in one step to ITAB2.


APPEND LINES OF ITAB1 TO ITAB2.


复制内表可以像其他数据类型一样使用MOVE语句。
Example:


Unacceptable:
* Entries: 100, Line width 100


REFRESH ITAB2.
LOOP AT ITAB1 INTO WA.
  APPEND WA TO ITAB2.
ENDLOOP.


acceptable:
* Entries: 100, Line width 100


ITAB2[] = ITAB1[].


对于sort语句,请指定排序的字段。
Example:


Unacceptable:
* Entries: 100, Line width: 500
* Key width: 492


SORT ITAB.


acceptable:
* Entries: 100, Line width: 500
* Key width: 20


SORT ITAB BY K.


其他语句
CASE语句
CASE 相对于IF语句更清晰易读,性能也稍好一些。
Example:


Unacceptable:
DATA C TYPE C.
 IF     C = 'A'.   WRITE '1'.
 ELSEIF C = 'B'.   WRITE '2'.
 ELSEIF C = 'C'.   WRITE '3'.
 ELSEIF C = 'D'.   WRITE '4'.
 ELSEIF C = 'E'.   WRITE '5'.
 ELSEIF C = 'F'.   WRITE '6'.
 ELSEIF C = 'G'.   WRITE '7'.
 ELSEIF C = 'H'.   WRITE '8'.
 ENDIF.


acceptable:
DATA C TYPE C.
 CASE C.
   WHEN 'A'. WRITE '1'.
   WHEN 'B'. WRITE '2'.
   WHEN 'C'. WRITE '3'.
   WHEN 'D'. WRITE '4'.
   WHEN 'E'. WRITE '5'.
   WHEN 'F'. WRITE '6'.
   WHEN 'G'. WRITE '7'.
   WHEN 'H'. WRITE '8'.
 ENDCASE.


WHILE语句
如果可以使用WHILE语句来替换DO+EXIT语句,那么请使用WHILE语句,因为WHILE语句更易读且性能也要好一些。
Example:


Unacceptable:
DATA C TYPE C. DATA I TYPE I.
 I = 0.
 DO.
    IF C NE SPACE. EXIT. ENDIF.
    ADD 1 TO I.
    IF I GT 10. C = 'X'. ENDIF.
 ENDDO.


Acceptable:
DATA C TYPE C. DATA I TYPE I.
 I = 0.
 WHILE C = SPACE.
    ADD 1 TO I.
    IF I GT 10. C = 'X'. ENDIF.
 ENDWHILE.


比较语句
比较语句中的两操作数尽量使用相同的数据类型,比如字符与字符的比较性能比字符与数字的比较更好。
Example:


Unacceptable:
DATA C(8) TYPE C.
 IF 12345678 = C.
 ENDIF.


Acceptable:
DATA C(8) TYPE C.
 IF '12345678' = C.
 ENDIF.


附录
SAP业务模块命名表
<XX>
业务模块
FIN
财务模块
MM
物资管理模块
SD
销售分销模块
PS
项目管理模块
PM
设备管理模块
QM
质检模块
OM
运行模块
HR
人资模块
BC
基础模块
CA
跨模块应用
SRM
SRM系统
数据字典对象类型命名表
<T>
类型
T
透明表
S
结构
E
数据元素
D

程序类型命名表


<Y>
程序类型代码
R
报表程序
P
对话程序
F
FORM/SMARTFORM打印程序
B
后台作业程序
I
公共Include程序
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值