关于内表之前写过很多文章,可以看下面的文章:
关于 LOOP + DELETE
在开发过程当中,有时候需要根据某些条件删除内表中的一些数据。
如果你直接在 LOOP 当中去执行 DELETE 操作,可能会引发程序 Dump,例如下面的代码:
LOOP AT ITAB INTO WA_ITAB.
read table itab2 into wa_itab2.
if sy-subrc is initial.
*I want to delete entries from ITAB.*
DELETE ITAB FROM WA_ITAB.
MODIFY ITAB.
endif.
ENDLOOP.
从正在循环的表中删除行不是一个好的选择。因为 SY-TABIX 很难被解释器正确捕捉。
工作原理
如果您正在处理内部表 itab,使用 LOOP ...ENDLOOP 语句,然后你想进行下面两个操作:
- 往表中插入新行
INSERT - 从表中删除现有行
DELETE
这些更改会自动在下一个 Loop Pass 上生效。
要了解循环处理中的 insert/delete 操作是如何工作的,请将内部 table 想象成 table 行的串联列表:
当前条目之后插入操作
- 当系统在当前表条目之后执行插入操作(使用
INSERT、APPEND或COLLECT执行)时,将在后续循环传递中处理新插入的行。
例如,如果您在第 47 个 Loop Pass 中并在第 48 行之前插入了新行,则下一个(即第 48 个)Loop Pass 将首先将新插入的行作为第 48 行处理,并将旧第 48 行(即插入新行之前的行)的处理留到下一个(即第 49 行)Loop Pass 变为第 49 行。
在下一个循环传递中,SY-TABIX 设置为 48,然后设置为 49。
在当前表条目之后插入行时,应注意代码会进行无限循环的风险。
当前条目之后删除操作
当系统在当前表条目之后执行删除操作(使用 DELETE 执行)时,已删除的行不会在后续循环传递中处理。
例如,如果你在第 47 个 Loop Pass 中并删除了第 48 行,则下一个(即第 48 个)Loop Pass 会将旧的第 49 行(即已删除行之后的行)作为新的第 48 行进行处理。
在下一个循环传递中,SY-TABIX 相应地设置为 48。
当前条目之前插入操作
当系统在当前表条目之前或之上执行 insert 操作(使用 INSERT 执行)时,内部循环计数器会相应地递增。
例如,如果你在第 47 个 Loop Pass 中,并在第 17、24 和 33 行之前插入了新行,则下一个(即第 48 个)Loop Pass 会将旧的第 48 行(即三个插入操作之前的第 48 行)作为新的第 51 行进行处理。
在下一个循环传递中,SY-TABIX 设置为 51。
当前条目之前或者之上删除操作
如果在当前表条目之前或之上执行删除操作(使用 DELETE 执行),则内部循环计数器值会相应减小。
例如,如果您在第 47 个 Loop Pass 中并删除了第 17、24 和 33 行,则下一个(即第 48 个)Loop Pass 会将旧的第 48 行(即三个删除操作之前的第 48 行)作为新的第 45 行进行处理。
在下一个循环传递中,SY-TABIX 设置为 45。
以下构造尤其会导致删除整个内部表,因为在使用 DELETE 语句时,始终是当前的第一行被删除:
LOOP AT itab INTO wa FROM n TO m.
DELETE itab.
ENDLOOP.
通过设置 Flag,然后进行 DELETE + WHERE 操作
在 FILTER 关键字出现之前,我会选择在内表中定义个 flag 字段,当满足某种条件的时候,将这个值设定为 X,最后 LOOP 完之后,通过 DELETE + WHERE 进行删除,代码示例如下:
LOOP AT itab INTO wa_itab.
DATA(lv_tabx) = sy-tabix.
READ TABLE itab2 INTO wa_itab2 WITH KEY field = wa_itab-field.
IF sy-subrc = 0.
wa_itab-flag = 'X'.
MODIFY itab INDEX lv_tabx FROM wa_itab TRANSPORTING flag.
ENDIF.
CLEAR lv_tabx.
ENDLOOP.
DELETE itab WHERE flag = 'X'.
FILTER 表达式
新的 FILTER 运算符可以对内表进行两种过滤。语法:
... FILTER type( itab [EXCEPT] [IN ftab] [USING KEY keyname]
WHERE c1 op f1 [AND c1 op f2 [...]] ) ...
FILTER with single values
在这一变体中,您只需将内部表格中的行提取到满足简单值条件的表格结果中。
DATA(extract) =
FILTER #( spfli_tab USING KEY carr_city
WHERE carrid = CONV #( to_upper( carrid ) ) AND
cityfrom = CONV #( to_upper( cityfrom ) ) ).
前提条件是,过滤后的表必须有一个排序键或哈希键(主键或次键),该键在 WHERE 后面进行评估。您还可以通过使用 EXCEPT 来提取不满足 WHERE 条件的行。 当然,您也可以通过在 VALUE 或 REDUCE 表达式中使用 FOR 来达到同样的效果,但 FILTER 可以让您代码写得更短,速度也更快。
FILTER with filter table
在这一变体中,您将一个表中的行与另一个表(过滤表)中的内容进行比较,然后提取至少找到一个匹配项的那些行(类似 FOR ALL ENTRIES 的功能)。
TYPES: BEGIN OF filter,
cityfrom TYPE spfli-cityfrom,
cityto TYPE spfli-cityto,
END OF filter,
filter_tab TYPE HASHED TABLE OF filter
WITH UNIQUE KEY cityfrom cityto.
DATA(filter_tab) = ...
DATA(extract) =
FILTER #( spfli_tab IN filter_tab
WHERE cityfrom = cityfrom AND cityto = cityto ).
在这里,过滤表(也可以作为功能方法调用指定)必须有一个排序键(SORTED TABLE)或散列键(HASHED TABLE)(主键或次键),并对其进行评估。
FILTER 实现删除
假设有一个内表为:
那么使用又 Flag 的方式如下:
TYPES: BEGIN OF ty_itab,
a TYPE i,
b TYPE i,
c TYPE i,
flag TYPE c,
END OF ty_itab.
DATA: itab TYPE SORTED TABLE OF ty_itab WITH UNIQUE KEY a.
itab = VALUE #(
( a = 2021 b = 2 c = 3 )
( a = 2022 b = 22 c = 33 )
( a = 2023 b = 222 c = 333 )
).
cl_demo_output=>display( itab[] ).
LOOP AT itab INTO DATA(wa_itab).
IF wa_itab-a = 2021.
wa_itab-flag = 'X'.
MODIFY itab FROM wa_itab TRANSPORTING flag.
ENDIF.
ENDLOOP.
DELETE itab WHERE flag = 'X'.
cl_demo_output=>display( itab[] ).
删除掉 A = 2021 那一行数据,运行结果为:
那么使用 FILTER 的方式就可以使用如下的方式,而不用定义 Flag 字段:
itab = FILTER #( itab WHERE a <> 2021 ).
cl_demo_output=>display( itab[] ).
删除前:
过滤删除后:
附:内表表达式 VALUE 赋值
内表表达式 itab[ ...] 不支持 sy-subrc。到目前为止,如果找不到方括号中指定的表格行,就会出现异常。并不是每个人都喜欢这种做法。
作为一种变通方法,可以将表格表达式放在包含 OPTIONAL 或 DEFAULT 附加信息的 VALUE 或 REF 表达式中。如果找不到行,OPTIONAL 附加项会返回一个初始行,而 DEFAULT 附加项会返回一个给定值,该值可以指定为一个表达式,尤其是另一个表表达式。
TYPES:
BEGIN OF line,
id TYPE i,
value TYPE string,
END OF line,
itab TYPE SORTED TABLE OF line WITH UNIQUE KEY id.
DATA(def) = VALUE line( id = 0 value = `not found` ).
...
DATA(result) = VALUE #( itab[ id = ... ] DEFAULT def ).
参考链接: