1. 数据库访问
1.1 OpenSQL中的新命令
- OpenSQL查询在7.4版之前就已经可以使用,但它相当有限,功能不如NativeSQL。然而,随着7.4版本的发布,出现了新的选项,例如,嵌入到select中的case语句和计算、内部连接改进等。
- 首先,让我们看看一些基本的OpenSQL select语句。
- 以下示例是OpenSQL中选择的示例:
TYPES:
BEGIN OF gy_material_sql,
matnr TYPE matnr,
mtart TYPE mtart,
matkl TYPE matkl,
END OF gy_material_sql. CONSTANTS: lc_amount TYPE char6 VALUE 'Test'.
DATA: lt_materials TYPE TABLE OF gy_material_sql. SELECT matnr, mtart, matkl
FROM mara
UP TO 10 ROWS
INTO CORRESPONDING FIELDS OF TABLE @lt_materials.
- 下面,您可以找到一种方法来检索与上面的OpenSQL示例相同的结果,但使用我们更熟悉的旧OpenSQL语法。
select matnr mtart matkl
from mara
up to 10 rows
into corresponding fields of table lt_materials.
- 正如您所看到的,新旧语法之间有很多相似之处。新的OpenSQL语法仍然使用熟悉的关键字(如select, insert, update, modify, delete等等),但也引入了一些新的关键字。本文将介绍其中一些新的关键字。
- 上面的示例已经显示了两个变化。第一个更改是select子句中列名称之间的逗号。放置的每个逗号都表示列之间的分隔,并将导致结果集中出现多个字段。
- 第二个变化是指定不带转义符@的主机变量已过时。因此,在指定各种主机变量时,始终使用转义字符@。
- 注意:当使用新OpenSQl语法的一个特性时,语法检查以严格模式执行,这比常规语法检查更严格地处理语句。这意味着您不能将旧的OpenSQL语法与新的OpenQSL语法结合起来。
- 以下章节将介绍OpenSQL语法和功能中的更多变化和新特性。
1.1.1 SELECT @HostLiterals and Constants
- 在SELECT子句中指定项目列表时,不限于表和视图的列名。select可以返回一组列值,其中包含主机变量值和文字常量。
TYPES:
BEGIN OF gy_material_sql,
matnr TYPE matnr,
mtart TYPE mtart,
matkl TYPE matkl,TEXT1 TYPE STRING,
flag TYPE char1,
END OF gy_material_sql. CONSTANTS: lc_amount TYPE char6 VALUE 'Test'.
DATA: lt_materials TYPE TABLE OF gy_material_sql.**Example code: Note the @ that becomes mandatory in the target list when it is in the selection list*
SELECT matnr, mtart, matkl, @lc_amount AS text1, ‘X’ AS flag
FROM mara
UP TO 10 ROWS
INTO CORRESPONDING FIELDS OF TABLE @lt_materials.
- Old logic for comparison:
select matnr mtart matkl ‘X’ AS flag
from mara
up to 10 rows
into corresponding fields of table lt_materials. LOOP AT lt_materials ASSIGNING <ls_material>.
<ls_material>-text1 = lc_amount.
ENDLOOP.
1.1.2 CASE Statements into SQL Queries
- 现在可以将case语句添加到select语句本身中:
TYPES: BEGIN OF gy_customer_tax_rate,
kunnr TYPE kunnr,
name1 TYPE name1_gp,
ort01 TYPE ort01_gp,
tax_percentage TYPE i,
END OF gy_customer_tax_rate. DATA: lt_customer_tax_rate TYPE TABLE OF gy_customer_tax_rate. FIELD-SYMBOLS: <ls_customer_tax_rate> TYPE gy_customer_tax_rate. SELECT kunnr, name1, ort01,
CASE land1
WHEN 'GB' THEN 6
WHEN 'FR' THEN 21
ELSE 19
END AS tax_percentage
FROM kna1
UP TO 10 ROWS
INTO CORRESPONDING FIELDS OF TABLE @lt_customer_tax_rate.
- 上例:当land1中的值等于“GB”时,我们将值6放入tax_percentage。对于值“FR”,我们将21放入tax_percentage。对于所有其他情况,我们将值19置于tax_percentage中。
- 您还可以注意到,在示例中,当您使用这种新奇的新功能时,必须在变量(或常量)前面放一个@符号,以便让编译器知道您不是在谈论数据库中的字段。您还必须在从数据库返回的字段之间使用逗号,并将INTO语句放在末尾。
- CASE语句的优点是,它允许您将计算某些条件逻辑的工作外包给数据库,而不是在应用程序服务器上执行这些计算。将此类计算推送到SAP HANA数据库中进行处理,可以大大提高性能。
- Old logic for comparison:
SELECT kunnr land1 name1 ort01
FROM kna1
UP TO 10 rows
INTO CORRESPONDING FIELDS OF table lt_customer_tax_rate. LOOP AT lt_customer_tax_rate ASSIGNING <ls_customer_tax_rate>.
CASE <ls_customer_tax_rate>-land1.
WHEN 'GB'.
<ls_customer_tax_rate>-tax_percentage = 6.
WHEN 'FR'.
<ls_customer_tax_rate>-tax_percentage = 21.
WHEN OTHERS.
<ls_customer_tax_rate>-tax_percentage = 19.
ENDCASE.
ENDLOOP.
1.1.3 Calculations within SQL Statements
- 7.4版的另一个新功能是能够在SQL语句中执行算术运算。在早期版本中,您必须首先从数据库中获取数据,然后执行计算。
DATA: lt_calculation_results TYPE TABLE OF gy_calcuations.
DATA: offset TYPE i VALUE 5. SELECT vbeln, posnr, ntgew, brgew, kwmeng,gewei,
brgew - ntgew AS tara,
CAST( ntgew AS FLTP ) / CAST( kwmeng AS FLTP ) AS ratio,
DIV( umvkz, umvkn ) AS netweightperunit,
MOD( umvkz, umvkn ) AS mod,
ABS( umvkz + umvkn ) AS sum,
@offset + ABS( umvkz + umvkn ) AS sumwithoffset
FROM vbap
INTO CORRESPONDING FIELDS OF TABLE @lt_calculation_results
UP TO 15 ROWS.
- 也可以在这些操作中使用cast语句来获取正确的类型。请注意,cast语句的目标字段需要与casted-to类型具有相同的类型。也可以使用DIV、MOD和ABS等功能。此外,还可以将主变量添加到计算中(ref@offset)。所有字段需要用逗号分隔,目标字段需要在AS之后定义。
- Old logic for comparison:
DATA: lv_nom type f.
DATA: lv_denom type f.FIELD-SYMBOLS: <ls_calculations> TYPE gy_calcuations. SELECT vbeln posnr ntgew brgew kwmeng gewei umvkz umvkn
FROM vbap
INTO CORRESPONDING FIELDS OF TABLE lt_calculation_results.LOOP AT lt_calculation_results ASSIGNING <ls_calculations>.
<ls_calculations>-tara = <ls_calculations>-brgew - <ls_calculations>-ntgew.
lv_nom = <ls_calculations>-ntgew.
lv_denom = <ls_calculations>-kwmeng.
<ls_calculations>-ratio = lv_nom - lv_denom.
<ls_calculations>-netweightperunit = <ls_calculations>-umvkz DIV <ls_calculations>-umvkn.
<ls_calculations>-mod = <ls_calculations>-umvkz MOD <ls_calculations>-umvkn.
<ls_calculations>-sum = <ls_calculations>-umvkz + <ls_calculations>-umvkn.
ENDLOOP.
1.1.4 Buffering improvements select single
- 通过在表DDIC中使用缓冲,可以显著减少数据库访问,从而提高性能。虽然缓冲已经存在很长时间了,但也存在一些问题。主要是,在很多情况下,缓冲区是旁路的,性能根本没有任何提高。例如,使用“select single”而不指定主键。
- 在7.02之前,人们通常通过将整个表从数据库读取到一个内部表中,然后进行排序以实现二进制搜索来解决这个问题。人们有时会有两个(或更多)内部表,根据不同的键进行不同的排序。从7.02开始,您可以在内部表上拥有二级索引,因此您只需要一个表。然而,从内存消耗的角度来看,这仍然是一个表太多了。
- 从7.4版开始,这不再是一个问题。如果在不指定主键的情况下对表执行SELECT SINGLE,然后运行ST05性能跟踪,则即使没有指定主键,也不会看到任何数据库访问。即使已经在表中定义了二级索引,也会神奇地考虑到该索引。现在,不仅在完全缓冲表上,而且在一般缓冲表上都考虑了二级索引,这是性能方面的一个巨大飞跃。
1.1.5 Inline Declarations behind INTO
- 你可能会喜欢这个。从7.40开始,SP08可以使用7.40引入的声明运算符DATA(…)放置内联声明,SP02位于INTO之后。不需要为容器定义类型,这将动态确定。
SELECT matnr, mtart, meins, brgew, ntgew
FROM mara
INTO TABLE @DATA(lt_material)
UP TO 20 ROWS.
- 注意没有类型定义的内联DATA用法!按照旧方法,需要表类型的类型定义或DDIC条目。接下来需要使用创建的类型完成表声明。在中,可以使用创建的内部表。为了得到相同的结果,这需要很多额外的代码。
- Old logic for comparison:
TYPES: BEGIN OF ty_material,
matnr TYPE matnr,
mtart TYPE mtart,
meins TYPE meins,
brgew TYPE brgew,
ntgew TYPE ntgew,
END OF ty_material. DATA: lt_material TYPE TABLE OF ty_material. REFRESH: lt_material. SELECT matnr mtart meins brgew ntgew
FROM mara
INTO TABLE lt_material
UP TO 20 ROWS.
- Inner Join Improvements
- 通常需要读取一个数据库表的每个字段以及另一个相关表中的一个或两个字段。这可以通过例如内部联接来实现。然而,列出主表中的所有字段有些繁琐,但以下示例显示了解决方案:
SELECT mara~*, makt~maktx
FROM mara
INNER JOIN makt ON mara~matnr EQ makt~matnr
INTO TABLE @DATA(lt_materials)
UP TO 20 ROWS
- 这里的魔术键是星号。然而,像往常一样,在这种情况下使用星号是一种简单的方法,但它会降低数据库性能并导致内存消耗增加。
- 除了ABAP 7.4中的星号功能外,内部联接的语法也得到了扩展,因此现在您在定义表之间的关系方面有了更大的灵活性。
1.1.6 Extended on condition
- 现在还可以将中不在联接部分的主机变量或数据库字段添加到on条件中。以下是连接表时使用的新ON条件的示例。
SELECT so~buyer_guid,
bpa~company_name,
so~currency_code,
so~gross_amount + so~net_amount AS sum,
MAX( so~gross_amount + so~net_amount ) AS max,
CEIL( so~gross_amount ) AS ceiled_gross_amount,
CASE
WHEN so~gross_amount > 1000
THEN 'High volume sales order'
ELSE ' '
END AS volumn_order
FROM snwd_so AS so
RIGHT OUTER JOIN snwd_bpa AS bpa
ON so~buyer_guid = bpa~node_key AND so~gross_amount > 100000
GROUP BY so~gross_amount + so~net_amount, so~currency_code,
bpa~company_name, so~gross_amount, so~net_amount, so~buyer_guid
INTO TABLE @DATA(lt_result).
2. Declaring and Creating Variables
2.1 Omitting the Declaration of TYPE POOL Statements
- ABAP7.02中已经提供了此功能,但没有多少人知道它。TYPE-POOL语句(如abap和icon)不再需要在程序开始或顶部include中声明。这些常量现在在整个系统中是全局的,这意味着abap_true、abap_false和icon_material等可以在没有TYPE-POOL定义的情况下使用。在下面的示例中,我们直接使用abap_false和icon_material,之前没有任何声明。
DATA(lv_true_false) = abap_false.DATA(lv_icon) = icon_material.
# Old logic for comparison:TYPE-POOL: abap,icon.DATA: lv_true_false type flag.DATA: lv_icon type char30.lv_true_false = abap_false.lv_icon = icon_material.
2.2 Omitting Data Type Declarations
- 当用新的DATA语句声明一个新变量时,不需要定义变量类型,因为编译器可以通过查看要分配给这个变量的值来知道它必须是哪种数据类型。
DATA(lv_string) = 'This is a String. The next example is an integer'.
DATA(lv_integer) = 15.
- Old logic for comparison:
DATA: lv_string TYPE char50 VALUE 'This is a String. The next example is an integer '.
DATA: lv_integer TYPE i VALUE 15.
- 正如我们在上一节中看到的,也可以在select语句中省略主机变量的数据声明。
SELECT SINGLE matnr, mtart, meinsFROM maraINTO @DATA(ls_material).SELECT matnr, mtart, meins FROM maraINTO TABLE @DATA(lt_material)UP TO 20 ROWS.
- Old logic for comparison:
TYPES: BEGIN OF ty_material,matnr TYPE matnr,mtart TYPE mtart,meins TYPE meins,END OF ty_material.DATA: ls_material TYPE ty_material.DATA: lt_material TYPE TABLE OF ty_material.SELECT SINGLE matnr mtart meinsFROM maraINTO ls_material.SELECT matnr mtart meinsFROM maraINTO TABLE lt_material.
2.3 Filling Structures and Internal Tables while Creating Them
- 在ABAP 7.4之前,您只能创建一个变量,并在该变量是基本数据类型时为其提供初始值。例如:
DATA: lv_name TYPE string VALUE ‘FRED’.
- 但是,从7.4开始引入了VALUE语句,您可以对结构和内部表执行相同的操作。这样做的好处是,在使用新特性时,无需在声明结构或表后填充各个字段,从而节省了大量代码行。
TYPES gy_materials_sql TYPE STANDARD TABLE OF gy_material_sql WITH DEFAULT KEY.DATA(lt_material) = VALUE gy_materials_sql(( matnr = 'MAT01' mtart = 'FOOD' matkl = '1020' text1 = 'First material' )( matnr = 'MAT02' mtart = 'WASTE' matkl = '1030' text1 = 'Second material' )).
- Or another possibility is:
DATA lt_materials2 TYPE STANDARD TABLE OF gy_material_sql WITH DEFAULT KEY.lt_materials2 = VALUE #(( matnr = 'MAT01' mtart = 'FOOD' matkl = '1020' text1 = 'First material' )( matnr = 'MAT02' mtart = 'WASTE' matkl = '1030' text1 = 'Second material' )
).
- 但是,在上一个示例中,您在声明后将数据添加到本地表中。
- Old logic for comparison:
DATA: lt_materials TYPE TABLE OF gy_material_sql.DATA: ls_material TYPE gy_material_sql.ls_material-matnr = 'MAT01'.ls_material-mtart = 'FOOD'.ls_material-matkl = '1020'.ls_material-text1 = 'First material'.APPEND ls_material TO lt_materials.ls_material-matnr = 'MAT02'.ls_material-mtart = 'WASTE'.ls_material-matkl = '1030'.ls_material-text1 = 'Second material'.APPEND ls_material TO lt_materials.
- 对于旧逻辑,需要提供表和结构声明。然后用数据填充结构,并将此结构添加到表中。
2.4 Filling Internal Tables from Other Tables Using FOR
- 在上一节中,我们用硬编码的值填充了一个内部表。然而,大多数时候,您要么从数据库中填充内部表,要么从其他内部表中填充;几乎从来没有用硬编码的值填充它们。
- 使用ABAP 7.4中引入的FOR命令,现在可以用更优雅的方式从另一个表中填充内部表:表可以有不同的列,并且可以根据条件逻辑限制传输的内容。
- 在下面的示例中,我们首先创建一个包含两条记录的本地表。第二个本地表(lt_materials2)将填充来自第一个本地表的记录(lt_materials)。但是,只有mtart等于“FOOD”的记录才会复制到第二个本地表中。
TYPES gy_materials_sql TYPE STANDARD TABLE OF gy_material_sql WITH DEFAULT KEY.DATA(lt_material) = VALUE gy_materials_sql(( matnr = 'MAT01' mtart = 'FOOD' matkl = '1020' text1 = 'First material' )( matnr = 'MAT02' mtart = 'WASTE' matkl = '1030' text1 = 'Second material' )).DATA(lt_material2) = VALUE gy_materials_sql(FOR ls_material IN lt_material WHERE ( mtart EQ 'FOOD' )( matnr = ls_material-matnrmtart = ls_material-mtartmatkl = ls_material-matkltext1 = ls_material-text1)).
- Old logic for comparison:
DATA: lt_materials TYPE TABLE OF gy_material_sql.DATA: lt_materials2 TYPE TABLE OF gy_material_sql.DATA: ls_material TYPE gy_material_sql.ls_material-matnr = 'MAT01'.ls_material-mtart = 'FOOD'.ls_material-matkl = '1020'.ls_material-text1 = 'First material'.APPEND ls_material TO lt_materials.ls_material-matnr = 'MAT02'.ls_material-mtart = 'WASTE'.ls_material-matkl = '1030'.ls_material-text1 = 'Second material'.APPEND ls_material TO lt_materials.APPEND LINES OF lt_materials TO lt_materials2.DELETE lt_materials2 WHERE mtart NE 'FOOD'.
- 可以使用APPEND LINES OF…TO…语句将表记录从一个表复制到另一个表。然而,这不允许像新的FOR语句这样的附加条件逻辑,这意味着可能需要附加语句来删除不需要的行。
2.5 Creating Short-Lived Variables Using LET
- ABAP 7.4还允许您使用语句LET声明寿命较短的变量。用LET语句声明的变量仅在使用构造函数表达式创建数据时存在。这允许使用7.40之前还不可能实现的全新功能:声明短期局部变量。在LET语句外,语句结束后,这些变量(weapon_name等)不再存在,而不是像常规变量那样从例程内的任何位置访问。
DATA: lt_material TYPE TABLE OF gy_material_sql.DO 7 TIMES.DATA(ls_material) = VALUE gy_material_sql(LET index = sy-indexfood = 'FOOD' 临时变量IN matnr = 'MAT'mtart = foodmatkl = '1020'text1 = index).append ls_material TO lt_material.ENDDO.
3. String Processing
- ABAP 7.02和7.4版引入了字符串处理的一些相当大的更改。这些更改将在本节中讨论。
3.1 New String Features in Release 7.02
- 随着7.02版本的发布,出现了最新的字符串处理功能。最大的变化之一是现在管道和花括号可以用于字符串处理。管道用于指示管柱的起点和终点:
DATA lv_string TYPE string.lv_string = |This is a string|."Result: This is a string
- Old logic for comparison:
CONCATENATE 'Tis' 'is' 'a' 'string' into lv_string separated by space.# ORCONCATENATE 'This is a string' into lv_string.
DATA lv_larger TYPE char10 VALUE 'larger'.lv_string = |This is a { lv_larger } string with the result of a calculation: { 5 + 5 }|."Result: This is a larger string with the result of a calculation: 10.
- Old logic for comparison:
DATA(lv_calc_result) = 5 + 5.DATA lv_calc_result_c TYPE char2.WRITE lv_calc_result TO lv_calc_result_c.CONCATENATE 'This is a' lv_larger 'string with the result of a calculation:' lv_calc_result_c INTO lv_string SEPARATED BY space.
- 在7.02版中定义了许多新功能。以下小节将讨论这些新功能:
3.1.1 CMAX/CMIN
- 此函数最多可以比较9个类似字符的参数,并分别返回传递的最大值和最小值。
DATA: lv_result TYPE char4.lv_result = cmax( val1 = 'AAAC' val2 = 'AAAB' val3 = 'AAAD' )."Result: AAADlv_result = cmax( val1 = 'AAAC' val2 = 'AZAB' val3 = 'AAAD' )."Result: AZABlv_result = cmin( val1 = 'AAAC' val2 = 'AAAB' val3 = 'AAAD' )."Result: AAABlv_result = cmin( val1 = 'AAAC' val2 = '0AAC' val3 = 'AAAD' )."Result: 0AAC
3.1.2 Insert
- 使用insert方法,您可以将一个字符串插入另一个字符串。为此,还必须定义偏移量。
DATA lv_larger_string TYPE char30.lv_larger_string = insert( val = lv_stringsub = |larger |off = 10 ).
- Old logic for comparison:
CONCATENATE lv_string(9) 'larger' lv_string+10 into lv_larger_string SEPARATED BY space.
3.1.3 Condense
- 使用新的压缩函数,您现在还可以定义必须从字符串中删除哪些字符作为前导字符或尾随字符。
DATA lv_condense TYPE string.lv_condense = condense( val = ` XXThis is X stringXX`del = `X `from = `X` to = `a` )."lv_condense: This is a string
- 注意:Del、from和to是类似字符的表达式位置,如果它们具有固定长度,则忽略尾部空格。无法将“编号”位置放入del、from和to中。当指定了del、from和to的标准值时,construction函数的工作方式与condense语句相同,但不添加NO-GAPS。此外,from中的字符将替换为to中指定的字符串中的第一个字符。
3.1.4 Concat_lines_of
DATA: lt_tabel TYPE TABLE OF char100,result_concat TYPE string.APPEND 'This' TO lt_tabel .APPEND 'is' TO lt_tabel .APPEND 'a' TO lt_tabel .APPEND 'string' TO lt_tabel .result_concat = concat_lines_of( table = lt_tabel sep = ` ` )."result_concat: This is a string
3.1.5 Escape
- 方法escape支持使用转义字符替换基于规则的字符串。例如,当您要转换url时,可以使用此选项:
DATA(lv_url) = 'http://www.google.com'.DATA lv_url_esc TYPE string.lv_url_esc = escape( val = lv_url format = cl_abap_format=>e_url_full )."lv_url_esc: http%3A%2F%2Fwww.google.com
3.1.6 Repeat
- 此方法返回包含val内容的字符串,其次数与occ中指定的次数相同。
DATA lv_string_repeat TYPE string.lv_string_repeat = repeat( val = 'Test' occ = 5 )."lv_string_repeat: TestTestTestTestTest
3.1.7 Replace
DATA lv_result_replace TYPE string.lv_result_replace = replace( val = 'This XX a string' off = 4 len = 4 with = | IS | )."Result: ‘This is a string‘
3.1.8 Reverse
DATA lv_string_reverse TYPE string.lv_string_reverse = 'PABA'.lv_string_reverse = reverse( lv_string_reverse )."lv_string_reverse: ABAP
3.1.9 Shift_left/Shift_right.
- 这些新方法取代了我们熟悉的ABAP SHIFT函数,并使用SUB和循环参数提供了其他可能性。
DATA lv_result_shift TYPE string.lv_result_shift = shift_left( val = 'ABCD' places = 2 )."lv_result_shift: 'CD'lv_result_shift = shift_left( val = 'ABCD' circular = 2 )."lv_result_shift: 'CDAB'lv_result_shift = shift_left( val = 'ABCD' sub = 'AB' )."lv_result_shift: 'CD'
3.1.10 Substring
- 使用这些方法,可以从具有索引和字符位置的给定字符串中提取子字符串。
DATA lv_result_sub TYPE string.lv_result_sub = substring( val = 'ABCDEFGH' off = 3 len = 4 )."lv_result_sub: 'DEFG'lv_result_sub = substring_from( val = 'ABCDEFGH' sub = 'DEF' )."lv_result_sub: 'DEFGH'lv_result_sub = substring_after( val = 'ABCDEFGH' sub = 'DEF' )."lv_result_sub: 'GH'lv_result_sub = substring_before( val = 'ABCDEFGH' sub = 'DEF' )."lv_result_sub: 'ABC'lv_result_sub = substring_to( val = 'ABCDEFGH' sub = 'DEF' )."lv_result_sub: 'ABCDEF'
3.1.11 Case
DATA lv_result_case TYPE string.
Changes all the letters from the second character onwards lower case.lv_result_case = to_mixed( val = 'THIS IS A STRING' )."lv_result_case: 'This is a string'Changes all the letters from the second character onwards to higher case. Note when another higher case is already in the string al _ will be place before it. eg. THIS IS A STRING = T_H_I_S _I_S _A _S_T_R_I_N_Glv_result_case = from_mixed( val = 'This is a string' )."lv_result_case: 'THIS IS A STRING'Standard upper and lower case functionalitylv_result_case = to_upper( val = 'this is a string' )."lv_result_case: 'THIS IS A STRING'lv_result_case = to_lower( val = 'THIS IS A STRING' )."lv_result_case: 'this is a string'
3.1.12 Translate
- 这与ABAP命令TRANSLATE…USING…类似。此函数返回字符串,其中from中的每个字符都替换为TO中相同位置的字符。
DATA lv_result_translate TYPE string.lv_result_translate = translate( val = 'ABCDAB' from = 'AC' to = 'YZ' )."Result: 'YBZDYB'
3.1.13 Distance
- 这是一个有趣的方法。此方法返回两个字符串之间的“相似距离”(Levenshtein距离)。这是将一个字符串更改为另一个字符串所需的最小插入、删除和替换操作数,因此反映了两个字符串的相似性。
DATA lv_difference_between_strings TYPE i.lv_difference_between_strings = distance( val1 = 'Thisis a string' val2 = 'this is a string' )."lv_difference_between_strings: 2 (T has to changed to lower and SPACE have to be inserted)
3.2 New String Features in Release 7.4
- 6.2 7.4版中的新字符串功能7.4版中一个非常有趣且最有用的新字符串特性是,它允许您通过格式化选项删除前导零。在7.4版之前,我们必须定期调用函数CONVERSION_EXIT_ALPHA_INPUT和CONVERSION EXIT ALPHA_OUTPUT来添加和删除文档编号(如交货编号)中的前导零。在ABAP 7.4中,可以通过使用ALPHA格式化选项以更紧凑的方式完成,该选项与上面提到的两个功能模块做的事情完全相同。
DATA: lv_vbeln TYPE vbeln.lv_vbeln = '9561254'.lv_vbeln = |{ lv_vbeln ALPHA = IN }|. "Adds leading zeros to delivery for DB readSELECT SINGLE vbeln, ernam, erzet, erdat, bzirk, vstel, vkorg, lfartFROM likpINTO @DATA(ls_likp)WHERE vbeln EQ @lv_vbeln.lv_vbeln = |{ ls_likp-vbeln ALPHA = OUT }|. "Removes leading zeros for output
- Old logic for comparison:
DATA: ls_likp TYPE likp.DATA: lv_vbeln TYPE vbeln.lv_vbeln = '9561254'.CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'EXPORTINGinput = lv_vbelnIMPORTINGoutput = lv_vbeln.SELECT SINGLE vbeln ernam erzet erdat bzirk vstel vkorg lfartFROM likpINTO CORRESPONDING FIELDS OF ls_likpWHERE vbeln EQ lv_vbeln.CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'EXPORTINGinput = ls_likp-vbelnIMPORTINGoutput = lv_vbeln.
- ABAP 7.4中的另一个新字符串功能是可以使用&连接字符串:
DATA: lv_string_concat TYPE string.lv_string_concat = 'First' && ' and' && ' second' && ' string.'.
- Old logic for comparison:
DATA: lv_string_concat TYPE string.CONCATENATE 'First' 'and' 'second' 'string.' INTO lv_string_concat SEPARATED BY space.
4. Calling Functions
- 本节将讨论新的ABAP功能,这些功能使调用函数比过去更容易。
4.1 Method Chaining
- 方法链接在ABAP 7.02中已经可用,但这里也讨论了它,因为它鲜为人知。
- 在本文档中,您一直在阅读有关删除数据声明的内容。现在,我们都熟悉这样一种情况,即您有几十个助手变量,所有这些变量都存储中间步骤,并且您将一个方法调用的结果存储很短一段时间,仅用于将该值传递到另一个方法调用中。
- 在7.02版中,ABAP中出现了一个名为方法链接的新概念,通过它,您可以直接将一个方法的结果传递到另一方法的输入参数中,而不需要助手变量。这仅适用于返回一个变量的方法。
method CALCULATE_AVG_INT.rv_ret_int = ( IV_INT1 + IV_INT2 ) / 2.endmethod.DATA(lv_avg_int) = me->calculate_avg_int( iv_int1 = me->calculate_avg_int( iv_int1 = me->calculate_avg_int( iv_int1 = 5 iv_int2 = 3 ) iv_int2 = 2 ) iv_int2 = 1 ).
Old logic for comparison:DATA: lv_helper_int TYPE int4.DATA: lv_avg_int TYPE int4.lv_helper_int = me->calculate_avg_int( iv_int1 = 5 iv_int2 = 3 ).lv_helper_int = me->calculate_avg_int( iv_int1 = lv_helper_int iv_int2 = 2 ).lv_avg_int = me->calculate_avg_int( iv_int1 = lv_helper_int iv_int2 = 1 ).
4.2 Avoiding Type Mismatch Dumps when Calling Functions
- 调用函数模块或方法时,必须确保提供的每个变量都与预期的参数类型匹配。当您必须声明大量变量/参数时,这一过程可能需要相当长的时间。当错误地声明变量时,方法和函数模块中会出现语法错误,在运行时会出现短暂转储。
- 在ABAP 7.4中,一半的声明问题现在已经解决了:如果变量的唯一目的是从方法或函数接收值,那么可以内联声明变量,并且可以从参数定义中读取类型。此外,代码行更少,如果更改签名定义,结果变量将自动适应。
- Function module:
CALL FUNCTION 'CONVERSION_EXIT_ATINN_INPUT'EXPORTINGinput = 'Z_COLOR'IMPORTINGoutput = DATA(atinn_of_char_color).
- Old logic for comparison:
DATA lv_atinn_of_char_color TYPE atinn.CALL FUNCTION 'CONVERSION_EXIT_ATINN_INPUT'EXPORTINGinput = 'Z_COLOR'IMPORTINGoutput = lv_atinn_of_char_color.
me->calculate_avg_int( EXPORTINGiv_int1 = me-calculate_avg_int( iv_int1 = 5 iv_int2 = 3 ) iv_int2 = 1RECEIVINGrv_ret_int = DATA(lv_avg_string) ).
- Old logic for comparison:
DATA: lv_helper_int TYPE int4.DATA: lv_avg_string TYPE string.lv_helper_int = me->calculate_avg_int( iv_int1 = 5 iv_int2 = 3 ).lv_helper_int = me->calculate_avg_int( iv_int1 = lv_helper_int iv_int2 = 2 ).lv_avg_string = me->calculate_avg_int( iv_int1 = lv_helper_int iv_int2 = 1 ).
- 这种方法有几个优点:
- 代码行更少。
- 您可能无法获得类型不匹配错误或转储。
- 如果更改签名定义,则结果变量会相应地进行调整。
4.3 Using Constructor Operators to Convert Parameter Variables(使用构造函数运算符转换参数变量)
- 有时,我们需要转换一个例程转换的结果,然后才能将其传递给另一个例程。一个常见的例子是,通常您有一个字符串变量,您希望将该变量传递给只接受特定字符类型输入的函数(例如CHAR20)。在7.4之前,不能直接传递变量,必须将其移动到辅助变量中。在ABAP 7.4中,这可以通过使用特定类型的构造函数CONV来简化,CONV将值从一种类型转换为方法所需的类型,作为输入另一种类型。
method GET_A_STRING.rv_string = 'This is a simple string'.endmethod.DATA(lv_result) = me->accept_a_char20( conv char20( me->get_a_string( ) ) ).
DATA(lv_result) = me->accept_a_char20( conv #( me->get_a_string( ) ) ).
IF 1 / 3 > 0.
...
ENDIF.
IF CONV decfloat34( 1 / 3 ) > 0....
ENDIF.
IF ' ' = ` `.
...
ENDIF.
IF ' ' = CONV char1( ` ` )....
ENDIF.
DATA lv_int TYPE i.
lv_int = CONV i( sqrt( 5 ) ) + CONV i( sqrt( 6 ) ).
4.4 Functions That Expect TYPE REF TO DATA
- 导入函数和方法的参数时,通常需要特定类型的参数。然而,在某些情况下,我们直到运行时才知道变量类型。在这种情况下,实现所需功能的唯一方法是使用动态编程。在运行时才知道确切的数据类型的情况下,通常方法的导入参数类型为type REF TO data。这就是为什么
- 例如,当您调用构建动态签名的方法以传递到动态定义的方法时,就会发生这种情况。
- 在ABAP 7.40版中,现在可以使用构造函数运算符REF填充TYPE REF to DATA参数。因为运行时系统知道类型为ANY的变量的数据类型,REF#函数可以读取该类型并创建所谓的数据对象,然后在方法中传递该对象,该方法需要传递一个type REF TO data对象。
- 假设在本例中,iv_value是ANY类型的输入参数。REF的使用允许我们使用该值,而无需将其传递到动态创建的变量中。
IF iv_value IS SUPPLIED.me->accept_data_ref( REF #( iv_value ) ).
ENDIF.
- Old logic for comparison:
- 需要动态创建的变量才能将值传递给另一个方法:
DATA: lo_data_val TYPE REF TO data.IF iv_value IS SUPPLIED.CREATE DATA lo_data_val LIKE iv_value.GET REFERENCE OF iv_value INTO lo_data_val.me->accept_data_ref( lo_data_val ).ENDIF.
5. Conditional logic
- 在本章中,我们将进一步了解添加到逻辑表达式的新功能。
5.1 Functional Methods in Logical Expressions
- 由于ABAP 7.02版,因此可以在逻辑表达式语句中使用,如:
IF ( A + B) > ( C + D ).
…
ENDIF.
E = A + B
F = C + D
IF ( E ) > ( C ).
…
ENDIF.
- 上面的功能可能是您已经使用过的功能。然而,也可以在逻辑表达式中使用函数方法,这将有助于您避免使用助手变量。例如:
IF strlen( 'test' ) > 10."strlen of ‘test’ returns 4
ENDIF.
- strlen是SAP提供的函数方法,它将返回您提供的String的长度。7.02之前的旧逻辑用于比较:
DATA lv_length_text TYPE i.
lv_length_text = strlen( 'test' ).
IF lv_length_text > 10."strlen of lv_length_text equals 4
ENDIF.
5.2 Omitting ABAP_TRUE
- 当函数方法被引入ABAP时,部分想法是这样可以使代码可读。多年来,ABAP程序员的普遍共识是,如果要创建一个返回值的方法,该值指示某些内容是否正确,那么返回的参数应该键入ABAP_BOOL。
- 如果在逻辑条件中使用某个方法,并且该方法返回一个ABAP_BOOL类型的值,那么现在可以省略逻辑语句的第二部分。以下方法返回abap_true。
IF me->get_a_boolean( ) .DATA(lv_string) = 'The flag is true'.ELSE.lv_string = 'The flag is false'.ENDIF.
- Old logic for comparison:
IF me->get_a_boolean( ) EQ abap_true .lv_string = 'The flag is true'.ELSE.lv_string = 'The flag is false'.ENDIF.
- 注意,这个新特性只适用于返回一个ABAP_BOOL类型值的函数方法。例如,当函数方法返回字符串“ABAP_FALSE”时,此字符串值将导致NOT INITIAL,因此计算结果为true。
5.3 Using XSDBOOL as a Workaround for BOOLC
- 当将BOOLC函数的结果与abap_false进行比较时,如果BOOLC的返回值实际上为false,则会得到错误的结果。原因是BOOLC函数返回的值是一个字符串,将空字符串与abap_false进行比较将导致false。为了在ABAP 7.40中解决这个问题,引入了一个新的类似函数XSDBOOL。此函数将返回一个字符1作为X或空白,并允许与abap false进行正确的比较。
IF xsdbool( 1 = 2 ) = abap_false.DATA(lv_string) = 'The result is false'.ELSE.lv_string = 'The result is true'.ENDIF.
5.4 SWITCH instead of CASE
- CASE的问题是,您需要不断提到在CASE语句的每个分支中填充的变量。下面是一个case语句示例,它将把today的名称插入变量中。对于FM DATE_TO_DAY,这也是可能的,但这是一个简单的例子。
DATA: lv_indicator LIKE scal-indicator,lv_day TYPE char10.CALL FUNCTION 'DATE_COMPUTE_DAY'EXPORTINGdate = sy-datumIMPORTINGday = lv_indicator.
CASE lv_indicator.WHEN 1.lv_day = 'Monday'.WHEN 2.lv_day = 'Tuesday'.WHEN 3.lv_day = 'Wednesday'.WHEN 4.lv_day = 'Thursday'.WHEN 5.lv_day = 'Friday'.WHEN 6.lv_day = 'Saturday'.WHEN 7.lv_day = 'Sunday'.WHEN OTHERS.RAISE EXCEPTION TYPE zcx_day_problem.
ENDCASE.
- 在7.4中,可以通过使用新的SWITCH构造函数操作符稍微简化这一点。使用此运算符,您只需定义必须填充一次的变量:
DATA: lv_indicator LIKE scal-indicator.CALL FUNCTION 'DATE_COMPUTE_DAY'EXPORTINGdate = sy-datumIMPORTINGday = lv_indicator.DATA(lv_day) = SWITCH char10( lv_indicator
WHEN 1 THEN 'Monday'
WHEN 2 THEN 'Tuesday'
WHEN 3 THEN 'Wednesday'
WHEN 4 THEN 'Thursday'
WHEN 5 THEN 'Friday'
WHEN 6 THEN 'Saturday'
WHEN 7 THEN 'Sunday'
ELSE THROW zcx_day_problem( ) ).
- 从这个示例中可以看到,lv_day(本例中为CHAR10)的数据定义已经移动到表达式主体中,从而大大减少了所需的代码行。您还可以注意到,使用了THROW而不是RAISE EXCEPTION。用法是相同的,编译器对关键字RAISE EXCEPTION TYPE和THROW的求值方式相同。
5.5 COND Statement Instead of IF/ELSE or CASE
- CASE语句的另一个缺点是一次只能计算一个变量。因此,当您想扩展前一章中的CASE示例并检查当前语言是英语还是法语时,您必须将整个内容更改为if/ELSE结构。
- 然而,在7.4版中,您可以扩展前面的示例,并且可以通过使用COND构造函数操作符以更紧凑的方式实现这一点。
DATA: lv_indicator LIKE scal-indicator.CALL FUNCTION 'DATE_COMPUTE_DAY'EXPORTINGdate = sy-datumIMPORTINGday = lv_indicator.DATA(lv_day) = COND text30(
WHEN lv_indicator = 1 THEN 'Monday'
WHEN lv_indicator = 2 THEN 'Tuesday'
WHEN lv_indicator = 3 THEN 'Wednesday'
WHEN lv_indicator = 4 THEN 'Thursday'
WHEN lv_indicator = 5 THEN 'Friday'
WHEN lv_indicator = 6 THEN 'Saturday'
WHEN lv_indicator = 7 AND sy-langu EQ 'E' THEN 'Sunday'
WHEN lv_indicator = 7 AND sy-langu EQ 'F' THEN 'Dimanche'
ELSE THROW zcx_day_problem( ).
- Old logic for comparison, with CASE:
DATA: lv_indicator LIKE scal-indicator,lv_day TYPE char10.CALL FUNCTION 'DATE_COMPUTE_DAY'EXPORTINGdate = sy-datumIMPORTINGday = lv_indicator.
CASE lv_indicator.WHEN 1.lv_day = 'Monday'.WHEN 2.lv_day = 'Tuesday'.WHEN 3.lv_day = 'Wednesday'.WHEN 4.lv_day = 'Thursday'.WHEN 5.lv_day = 'Friday'.WHEN 6.lv_day = 'Saturday'.WHEN 7.IF sy-langu EQ 'E'.lv_day = 'Sunday'.ELSE.lv_day = 'Dimanche'.ENDIF.WHEN OTHERS.RAISE EXCEPTION TYPE zcx_day_problem.
ENDCASE.
6. Internal Tables
- 在本节中,您将了解一些与内部表相关的新ABAP功能。
6.1 Using Secondary Keys to Access a Internal Table in Multiple Ways(使用辅助键以多种方式访问内部表)
- ABAP 7.02也提供了此功能,但我们也将在这里讨论它,因为它鲜为人知,所以在这里讨论,因为它很少为人知。
- 传统上,如果您想以两种不同的方式(例如,通过名称或ID查找用户)以高性能读取内部表,那么您要么必须在读取之前对表进行排序,这是浪费处理时间,要么让两个相同的表进行不同的排序,这也是浪费内存。
- 在下面的示例中,我们用主键和次键定义了一个内部表,并从数据库表填充内部表。在第一次读取中,我们使用主键读取表。在第二次读取中,我们使用二级索引读取表。
DATA: lt_mara TYPE HASHED TABLE OF mara WITH UNIQUE KEY matnrWITH NON-UNIQUE SORTED KEY sort COMPONENTS bismt.DATA: ls_mara TYPE mara.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE lt_maraUP TO 10 ROWS.READ TABLE lt_mara INTO ls_mara WITH TABLE KEY matnr = '000000000000023683'.IF sy-subrc EQ 0."Do somethingENDIF.READ TABLE lt_mara INTO ls_mara WITH KEY sort COMPONENTS bismt = '1149700'.IF sy-subrc EQ 0."Do somethingENDIF.
- Old logic for comparison:
- 对于需要使用LOOP WHERE的旧逻辑,这比READ TABLE操作的性能差。通过复制内部表并按主键或次键对其进行排序,也可以以高效的方式使用读取表操作。
DATA: lt_mara TYPE HASHED TABLE OF mara.DATA: ls_mara TYPE mara.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE lt_maraUP TO 10 ROWS.LOOP AT lt_mara INTO ls_mara WHERE matnr = '000000000000023683'."Do somethingENDLOOP.LOOP AT lt_mara INTO ls_mara WHERE bismt = '1149700'."Do somethingENDLOOP.
6.2 Table Work Areas
- 不久前,SAP规定内部表中的标题行是魔鬼干的。这是因为使用标题行导致程序中存在两个名称完全相同的数据对象,这被视为混淆。现在您必须显式声明一个变量作为标题行。例如:
DATA: lt_table TYPE TABLE OF vbap .
DATA: ls_table LIKE LINE OF lt_table.
- 好消息是,现在您可以避免进行额外的变量声明,仍然有两个不同名称的变量,一个用于表,另一个用于工作区。在ABAP 7.4版中,有一种新的语法用于读取工作区和循环表:
- 示例如下:
READ TABLE lt_vbap WITH KEY vbeln = lv_vbeln INTO DATA(ls_vbap).
Example loop:
LOOP AT lt_vbap INTO DATA(ls_vbap).
…
ENDLOOP.
Old logic for comparison:
Example read:
DATA: ls_vbap LIKE LINE OF lv_vbap.
READ TABLE lt_vbap WITH KEY vbeln = lv_vbeln INTO ls_vbap.
Example loop:
DATA: ls_vbap LIKE LINE OF lv_vbap.
LOOP AT lt_vbap INTO ls_vbap.
…
ENDLOOP.
- 需要注意的一点非常重要,如果您声明了一个工作区,然后循环通过一个内部区域,在循环结束后,工作区将被处理的内部表的最后一行的值填充。在本节讨论内联声明的情况下,变量也将被处理的内部表的最后一行的值填充。
6.3 Reading from a Table
- 在ABAP 7.4中,您可以从read语句中删除关键字,并用一对更像计算机的方括号[]替换英文短语read TABLE。此新语法使单个变量值只需一行代码即可直接读取:
DATA lv_vbeln TYPE vbeln VALUE '1'.
DATA lv_posnr TYPE posnr VALUE '10'.
DATA(lv_matnr) = lt_vbap[ vbeln = lv_vbeln posnr = lv_posnr ]-matnr.
- 正如您在上面的示例中看到的,我们可以直接从lt_vbap表中读取的结构中检索材质编号。我们甚至不需要为结构创建一个helper变量。
- Old logic for comparison:
DATA lv_vbeln TYPE vbeln VALUE '1'.
DATA lv_posnr TYPE posnr VALUE '10'.
DATA lv_matnr TYPE matnr.
DATA ls_vbap LIKE LINE OF lt_vbap.
READ TABLE lt_vbap INTO ls_vbap WITH KEY vbeln = lv_vbeln posnr = lv_posnr.
lv_matnr = ls_vbap-matnr.
- 当新语法找不到要查找的内部表行时,会引发异常。SAP肯定收到了很多关于这方面的投诉,因为从7.4版(SP 8)开始,他们提供了一个解决方法:如果在查询末尾添加单词OPTIONAL,那么系统突然不关心是否没有找到记录,并且会返回您要查找的任何数据类型的初始值。
DATA(lv_matnr) = lt_vbap[ vbeln = lv_vbeln posnr = lv_posnr ]-matnr OPTIONAL.
- Old logic for comparison:
DATA lv_vbeln TYPE vbeln VALUE '1'.
DATA lv_posnr TYPE posnr VALUE '10'.
DATA lv_matnr TYPE matnr.
DATA ls_vbap LIKE LINE OF lt_vbap.
READ TABLE lt_vbap INTO ls_vbap WITH KEY vbeln = lv_vbeln posnr = lv_posnr.
IF sy-subrc EQ 0.lv_matnr = ls_vbap-matnr.
ENDIF.
- 当您没有直接从结构中读取特定值时,可以在读取后检查SY-SUBRC eq 4,以检查读取是否失败。
6.4 CORRESPONDING of Internal Tables
- 7.4中新增了一个名为CORRESPONDING的构造函数运算符,前面没有MOVE一词。此运算符将两个内部表之间的数据移动到一个全新的级别。假设您有两个内部表,其中充满了相关数据,但它们是由一组列定义的,其中一些列的定义有所不同。您要做的是将具有相同名称的字段从一个表复制到另一个表,并将具有不同名称的字段从表的一列复制到其他表的正确列。
- 此外,一些SAP文档建议永远不要使用MOVE-CORRESPONDING,因为它在移动具有不同类型的类似命名字段时可能会导致问题,例如整数字段中的字符串字段。新关键字CORRESPONDING通过允许用户定义移动条件来解决这个可能的问题。使用CORRESPONDING语句时,可以传递MAPPING和EXCEPTions。
- 在下面的示例中,我们定义了一个具有特殊字段命名的结构,并创建了一个表。然后,我们用一些记录填充内联声明的表,并使用相应的方法将填充的表移动到特殊定义的表中。关键字映射用于映射不对应的字段,except用于不传输字段bismt。
TYPES: BEGIN OF ty_mat_spec,mat_nr TYPE matnr,mat_type TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_mat_spec.DATA: lt_mat_spec TYPE TABLE OF ty_mat_spec.SELECT matnr, mtart, matkl, bismtFROM maraINTO TABLE @DATA(lt_mara)UP TO 10 ROWS.lt_mat_spec = CORRESPONDING #( lt_mara MAPPING mat_nr = matnrmat_type = mtartEXCEPT bismt ).
- Old logic for comparison:
- 相同结果的许多附加代码。在循环第一个表时,需要额外的数据声明将字段从一个表移动到另一个表。
TYPES: BEGIN OF ty_mat_spec,mat_nr TYPE matnr,mat_type TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_mat_spec.DATA: lt_mara TYPE TABLE OF mara.DATA: ls_mara TYPE mara.DATA: lt_mat_spec TYPE TABLE OF ty_mat_spec.DATA: ls_mat_spec TYPE ty_mat_spec.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE lt_maraUP TO 10 ROWS.LOOP AT lt_mara INTO ls_mara.CLEAR: ls_mat_spec.ls_mat_spec-mat_nr = ls_mara-matnr.ls_mat_spec-mat_type = ls_mara-mtart.ls_mat_spec-matkl = ls_mara-matkl.APPEND ls_mat_spec TO lt_mat_spec.ENDLOOP.
6.5 MOVE-CORRESPONDING for Internal Deep Structured Tables
- 深层内部表是具有自身包含结构或表类型的结构类型的表。在7.40之前,不可能在这种类型的两个内部表之间直接执行MOVE-CORRESPONDING。从发布版7.40起,MOVE-CorRESPONDIN可以用于内部表,但尚未解决可能的类型不匹配问题,因为它将一个内部表的内容转储到另一个表中,同时只查看表结构中每个组件的名称。
- 在下面的示例中,我们定义了一个深层结构并创建了一个表。然后,我们用一些记录填充深层内部表,并执行相应的从填充表到空表的移动。
TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_sales_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_sales_mats.ls_sales_mats-section = 'Sales'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_sales_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.MOVE-CORRESPONDING ls_sales_mats TO ls_prod_mats.
- Old logic for comparison:
- 在7.40之前,我们必须循环结构的内部表,以逐行复制其行。
TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_sales_mat.DATA: ls_sales_mat TYPE ty_sales_mat.DATA: ls_prod_mat TYPE ty_prod_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_sales_mats.ls_sales_mats-section = 'Sales'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_sales_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.MOVE ls_sales_mats-section TO ls_prod_mats-section.LOOP AT ls_sales_mats-t_materials INTO ls_sales_mat.MOVE-CORRESPONDING ls_sales_mat TO ls_prod_mat.APPEND ls_prod_mat TO ls_prod_mats-t_materials.ENDLOOP.
6.5.1 MOVE-CORRESPONDING EXPANDING NESTED TABLES
- 在MOVE-CORRESPONDING EXPANDING NESTED TABLES中,将删除目标内部表中可能出现的任何内容。具有简单值(如数字)的列被复制到另一个表中同名的字段中。对于深层结构,只复制相同的名称字段,而不需要它们处于相同的顺序。
- 在下面的示例中,我们定义了一个深层结构,并创建了一个使用该结构的表。我们还创建了第二个深层结构(不同的顺序),并使用此结构创建了一个表。我们填充这两个本地表,并使用move对应将数据从一个表传输到另一个表。第二个表中的数据将被删除,并替换为我们要从中移动的表的数据。
TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,matnr TYPE matnr,bismt TYPE bismt,mtart TYPE mtart,matkl TYPE matkl,END OF ty_sales_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,t_materials TYPE TABLE OF ty_prod_mat,section TYPE char10,END OF ls_sales_mats.ls_sales_mats-section = 'Sales'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_sales_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.ls_prod_mats-section = 'Production'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_prod_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.MOVE-CORRESPONDING ls_sales_mats TO ls_prod_mats EXPANDING NESTED TABLES.
----TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,mtart TYPE mtart,matnr TYPE matnr,END OF ty_sales_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,s_material TYPE ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,section TYPE char10,t_materials TYPE TABLE OF ty_sales_mat,s_material TYPE ty_prod_mat,END OF ls_sales_mats.DATA: BEGIN OF ls_prod_matss,a(10) TYPE c,t_matss LIKE TABLE OF ls_prod_mats,END OF ls_prod_matss.DATA: BEGIN OF ls_sales_matss,a(10) TYPE c,t_matss LIKE TABLE OF ls_sales_mats,END OF ls_sales_matss.ls_sales_matss-a = '1'.ls_sales_matss-t_matss = VALUE #( ( section = '2' t_materials = VALUE #( ( matnr = 'M1' mtart = 'Z001' ) ) ) )."ls_sales_matss-t_matss = VALUE #( ( section = '2' ) ).ls_prod_matss-a = '11'.ls_prod_matss-t_matss = VALUE #( ( section = '22't_materials = VALUE #( ( matnr = 'M11' mtart = 'Z002' ) )s_material = VALUE #( matnr = 'M21' mtart = 'Z022' ) ) ).MOVE-CORRESPONDING ls_sales_matss TO ls_prod_matss EXPANDING NESTED TABLES.
- Old logic for comparison:
- 在7.40之前,我们必须循环结构的内部表,逐行复制其行
TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,matnr TYPE matnr,bismt TYPE bismt,mtart TYPE mtart,matkl TYPE matkl,END OF ty_sales_mat.DATA: ls_sales_mat TYPE ty_sales_mat.DATA: ls_prod_mat TYPE ty_prod_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,t_materials TYPE TABLE OF ty_prod_mat,section TYPE char10,END OF ls_sales_mats.ls_sales_mats-section = 'Sales'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_sales_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.ls_prod_mats-section = 'Production'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_prod_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.CLEAR: ls_prod_mats.MOVE ls_sales_mats-section TO ls_prod_mats-section.LOOP AT ls_sales_mats-t_materials INTO ls_sales_mat.MOVE-CORRESPONDING ls_sales_mat TO ls_prod_mat.APPEND ls_prod_mat TO ls_prod_mats-t_materials.ENDLOOP.
6.5.2 MOVE-CORRESPONDING KEEPING TARGET LINES
- 在“移动-响应-保持目标线”中发生的事情很有趣;如果目标内部表中已经有任何内容,那么它将保留在那里。因此,您不会覆盖目标表中的任何数据。然后,在目标表的末尾,添加额外的行,如果您只是在两个表之间执行MOVE-CORRESPONDING,就会发生这种情况。这与内部表1的APPEND LINES OF internal table 1 TO internal table 2非常相似,只是这两个表的结构不同,并且只会遇到名称相同的组件。目标结构中定义的字段也将保留在那里,并且不会被源结构覆盖,例如下面的示例中的节字段。
TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,matnr TYPE matnr,bismt TYPE bismt,mtart TYPE mtart,matkl TYPE matkl,END OF ty_sales_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,t_materials TYPE TABLE OF ty_prod_mat,section TYPE char10,END OF ls_sales_mats.ls_sales_mats-section = 'Sales'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_sales_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.ls_prod_mats-section = 'Production'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_prod_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.MOVE-CORRESPONDING ls_sales_mats-t_materials TO ls_prod_mats-_materials KEEPING TARGET LINES.
- Old logic for comparison:
TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,matnr TYPE matnr,bismt TYPE bismt,mtart TYPE mtart,matkl TYPE matkl,END OF ty_sales_mat.DATA: ls_sales_mat TYPE ty_sales_mat.DATA: ls_prod_mat TYPE ty_prod_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,t_materials TYPE TABLE OF ty_prod_mat,section TYPE char10,END OF ls_sales_mats.ls_sales_mats-section = 'Sales'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_sales_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.ls_prod_mats-section = 'Production'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_prod_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.LOOP AT ls_sales_mats-t_materials INTO ls_sales_mat.“note, no move of the simple value fields in the structure.APPEND ls_prod_mat TO ls_prod_mats-t_materials.ENDLOOP.
6.5.3 MOVE-CORRESPONDING EXPANDING NESTED TABLES KEEPING TARGET LINES
- 正如可以想象的那样,MOVE-CORRESPONDING EXPANDING NESTED TABLES KEEPING TARGET LINES 同时执行前面的两项任务;它的作用类似于APPEND LINES OF internal table 1 TO internal table 2,只将名称相同的组件复制到新行,还复制更深层次的结构。保留目标结构中简单字段的值。
TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,matnr TYPE matnr,bismt TYPE bismt,mtart TYPE mtart,matkl TYPE matkl,END OF ty_sales_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,t_materials TYPE TABLE OF ty_prod_mat,section TYPE char10,END OF ls_sales_mats.ls_sales_mats-section = 'Sales'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_sales_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.ls_prod_mats-section = 'Production'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_prod_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.MOVE-CORRESPONDING ls_sales_mats-t_materials TO ls_prod_mats-t_materials EXPANDING NESTED TABLES KEEPING TARGET LINES.
- Old logic for comparison:
TYPES: BEGIN OF ty_prod_mat,matnr TYPE matnr,mtart TYPE mtart,matkl TYPE matkl,bismt TYPE bismt,END OF ty_prod_mat.TYPES: BEGIN OF ty_sales_mat,matnr TYPE matnr,bismt TYPE bismt,mtart TYPE mtart,matkl TYPE matkl,END OF ty_sales_mat.DATA: ls_sales_mat TYPE ty_sales_mat.DATA: ls_prod_mat TYPE ty_prod_mat.DATA: BEGIN OF ls_prod_mats,section TYPE char10,t_materials TYPE TABLE OF ty_prod_mat,END OF ls_prod_mats.DATA: BEGIN OF ls_sales_mats,t_materials TYPE TABLE OF ty_prod_mat,section TYPE char10,END OF ls_sales_mats.ls_sales_mats-section = 'Sales'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_sales_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.ls_prod_mats-section = 'Production'.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE ls_prod_mats-t_materialsUP TO 3 ROWSWHERE matkl = '7030'.LOOP AT ls_sales_mats-t_materials INTO ls_sales_mat.
“note, no move of the simple value fields in the structure. Move corresponding is only required when copying also deep structures have to be copied. What is not the case in this exampleAPPEND ls_prod_mat TO ls_prod_mats-t_materials.ENDLOOP.
6.6 New Functions for Common Internal Table Tasks
- 在本节中,我们将看到一些新的帮助函数,用于从内部表中检索信息。
6.6.1 Line Index
- 在使用内部表时,有些情况下您希望确切地知道您感兴趣的数据存储在内部表的哪一行中。要在7.4版之前的ABAP中找到这些信息,您应该编写代码,声明一个helper变量来存储目标记录的行号,读取表的唯一目的就是查找行号,然后将包含表读取结果的系统变量转移到helper变量中。
- 在7.4版中,这可以通过使用内置函数LINE_INDEX来简化,该函数执行完全相同的任务,但不需要查看SY-SUBRC和SY-TABIX的值。
SELECT matnr, mtart, matkl, bismtFROM maraINTO TABLE @DATA(lt_mara)UP TO 10 ROWS."Retrieve the row number for a specific itemDATA(lv_tabix) = line_index( lt_mara[ matnr = '000000000000024344' ] ).
- Old logic for comparison:
DATA: lt_mara TYPE TABLE OF mara.DATA: lv_tabix TYPE sytabix.DATA: lv_exist TYPE flag.* get some data in the internal tableSELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE lt_maraUP TO 10 ROWS."Retrieve the row number for a specific itemREAD TABLE lt_mara WITH KEY matnr = '000000000000024344' TRANSPORTING NO FIELDS.IF sy-subrc EQ 0.lv_tabix = sy-tabix.ENDIF.
6.6.2 Line Exist
- 假设您想查看内部表是否已经有您想要添加的条目,或者您只想知道该条目是否已经添加。通过使用新的内置函数LINE_EXISTS,您可以轻松检查您搜索的条目是否已经在内部表中。
SELECT matnr, mtart, matkl, bismtFROM maraINTO TABLE @DATA(lt_mara)UP TO 10 ROWS."Check for line existenceIF line_exists( lt_mara[ matnr = '000000000000024344' ] )."Do somethingENDIF.
- Old logic for comparison:
DATA: lt_mara TYPE TABLE OF mara.DATA: lv_tabix TYPE sytabix.DATA: lv_exist TYPE flag.* get some data in the internal tableSELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE lt_maraUP TO 10 ROWS."Check for line existenceREAD TABLE lt_mara WITH KEY matnr = '000000000000024344' TRANSPORTING NO FIELDS.IF sy-subrc EQ 0.lv_exist = abap_true.ENDIF.IF lv_exist EQ abap_true."Do somethingENDIF.
- 使用LINE_EXISTS代替SY-SUBRC的优点更可靠。使用SY-SUBRC可能是不明智的,因为您永远不知道何时会有人在您的READ语句和评估SY-SUBR的IF语句之间插入一些代码。
6.7 Internal Table Queries with REDUCE
- 在ABAP 7.4中,添加了一个新的构造函数运算符REDUCE,可用于在内部表上执行特定逻辑并返回单个结果。要使用此运算符,首先必须定义要创建的变量以及该变量的类型,然后为结果变量设置初始值,最后循环表,执行定义的逻辑。例子:
SELECT matnr, mtart, matkl, bismtFROM maraINTO TABLE @DATA(lt_mara)UP TO 10 ROWS."Retrieve amount of records with material group 9000DATA(lv_records_match) = REDUCE sytabix(INIT lv_result = 0FOR ls_mara IN lt_maraNEXT lv_result = lv_result + me->check_mat_grp_9000( ls_mara-matkl ) ).
- Old logic for comparison:
DATA: lt_mara TYPE TABLE OF mara.DATA: ls_mara TYPE mara.DATA: lv_records_match TYPE sytabix.SELECT matnr mtart matkl bismtFROM maraINTO CORRESPONDING FIELDS OF TABLE lt_maraUP TO 10 ROWS."Retrieve amount of records with material group 9000LOOP AT lt_mara INTO ls_mara.lv_records_match = lv_records_match + me->check_mat_grp_9000( ls_mara-matkl ).ENDLOOP.
6.8 Grouping Internal Tables
- 您很可能熟悉可以添加到数据库SELECT中的GROUPBY加法,它将类似的记录压缩为一行。在ABAP 7.4之前,当您想做类似于内部表的事情时,您可能使用了COLLECT语句或非常狡猾的AT NEW语句,这在很多情况下都不能很好地工作。事实上,AT NEW语句是如此不可预测,所以最好完全避免它,并使用其他方法处理内部表中的相关块。
- 使用ABAP 7.4,现在可以将GROUP BY选项添加到内部表的循环中。循环中的GROUPBY为您提供了一条记录,该记录是一组单独的记录,此外,还可以将该记录再次分解为构成它的各个记录。例子:
SELECT aufnr, gamng, gmein, plnbezFROM afkoINTO TABLE @DATA(lt_afko)UP TO 100 ROWS.DATA: lt_group_lines LIKE lt_afko.
* Loop over the records grouped by material (PLNBEZ)LOOP AT lt_afko INTO DATA(ls_afko) GROUP BY ( plnbez = ls_afko- plnbez ) ASCENDING ASSIGNING FIELD-SYMBOL(<ls_group>). CLEAR: lt_group_lines.
"Loop over the grouped recordLOOP AT GROUP <ls_group> ASSIGNING FIELD-SYMBOL(<ls_group_line>). lt_group_lines = VALUE #( BASE lt_group_lines ( <ls_group_line> ) ).ENDLOOP.ENDLOOP.
- Old logic for comparison:
DATA: lt_afko TYPE TABLE OF afko.DATA: ls_afko TYPE afko.DATA: lt_afko_group TYPE TABLE OF afko.DATA: ls_afko_old TYPE afko.* Get the data from the DB tableSELECT aufnr gamng gmein plnbezFROM afkoINTO CORRESPONDING FIELDS OF TABLE lt_afkoUP TO 100 ROWS.* Sort the dataSORT lt_afko ASCENDING BY plnbez.* Loop over the data calculating a total for every material in ls_afko_old,
* and keeping another table containing the records with the same material
* this is somewhat more troublesome than the 7.40 GROUP BY in a loop.LOOP AT lt_afko INTO ls_afko.IF ls_afko_old IS INITIAL.ls_afko_old = ls_afko.ENDIF.IF ls_afko_old-plnbez <> ls_afko-plnbez.CLEAR: lt_afko_group.ELSE.ls_afko_old-gamng = ls_afko_old-gamng + ls_afko-gamng.APPEND ls_afko TO lt_afko_group.ENDIF.ENDLOOP.
- ABAP 7.4引入了两种新方法,都使用构造函数操作FILTER从另一个内部表中提取一个内部表格。
6.9.1 FILTER with Conditional Logic
- FILTER操作数可用于将数据从一个内部表提取到另一个。在7.4版之前,您必须使用条件逻辑编写一个循环,该循环将遍历一个内部表,并将该内部表的所需行附加到另一个内部表格。
- 然而,使用ABAP 7.4,现在也可以执行相同的操作,但只需更少的代码和FILTER操作数。注意:问题是7.4中引入的FILTER操作仅在您正在提取的表具有HASHED或SORTED键时有效。
DATA: lt_afko TYPE STANDARD TABLE OF afko WITH KEY aufnr WITH NON-UNIQUE SORTED KEY gamng COMPONENTS gamng.SELECT aufnr gamng gmein plnbezFROM afkoINTO CORRESPONDING FIELDS OF TABLE lt_afkoUP TO 100 ROWS.* Then filter this data in a new table for all entries that have a quantity higher than 10DATA lv_gamng_lower TYPE afko-gamng VALUE '10.000'.DATA(lt_afko_filtered) = FILTER #( lt_afko USING KEY gamngWHERE gamng > lv_gamng_lower ).
- Old logic for comparison:
* first declare the required data containersDATA: lt_afko TYPE TABLE OF afko.DATA: ls_afko TYPE afko.DATA: lt_afko_filtered TYPE TABLE OF afko.DATA lv_gamng_lower TYPE afko-gamng VALUE '10.000'.* Get the data from the DB tableSELECT aufnr gamng gmein plnbezFROM afkoINTO CORRESPONDING FIELDS OF TABLE lt_afkoUP TO 100 ROWS.* Filter the data in a loopLOOP AT lt_afko INTO ls_afko.IF ls_afko-gamng > lv_gamng_lower.append ls_afko to lt_afko_filtered.ENDIF.ENDLOOP.
6.9.2 FILTER Used as a FOR ALL ENTRIES
- 当从数据库读入内部表时,如果您不想将所有数据都放在一个大型表中,或者无法通过内部联接将所有表合并在一起,那么解决方案是执行FOR all ENTRIES。使用ABAP 7.4版,您现在可以在内部表而不是数据库上执行相当于FOR ALL ENTRIES的操作。
DATA: lt_aufnr_set TYPE SORTED TABLE OF gy_aufnr WITH UNIQUE KEY aufnr.* First get the DATA with the selectsSELECT aufnr, gamng, gmein, plnbezFROM afkoINTO TABLE @DATA(lt_tab_filter_all)UP TO 100 ROWS.* Get 50 order numbersSELECT aufnrFROM afkoINTO TABLE lt_aufnr_setUP TO 50 ROWS.* Now use the filter statement to get the records of the first internal table using the order numbers of the second internal tableDATA(lt_filtered_afko) = FILTER #( lt_tab_filter_all IN lt_aufnr_setWHERE aufnr EQ aufnr ).
- Old logic for comparison:
DATA: lt_aufnr_set TYPE TABLE OF gy_aufnr.DATA: lt_afko TYPE TABLE OF afko.* Get 50 order numbersSELECT aufnrFROM afkoINTO TABLE lt_aufnr_setUP TO 50 ROWS.* Select the additional data using a for all entries, with a table emptyness checkIF lt_aufnr_set[] IS NOT INITIAL.SELECT aufnr gamng gmein plnbezFROM afkoINTO CORRESPONDING FIELDS OF TABLE lt_afkoFOR ALL ENTRIES IN lt_aufnr_setWHERE aufnr EQ lt_aufnr_set-aufnr.ENDIF.