关于 ABAP 内表的更多功能之 FILTER 实现 LOOP + DELETE

813 阅读6分钟

关于内表之前写过很多文章,可以看下面的文章:

关于 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 行的串联列表:

当前条目之后插入操作

  • 当系统在当前表条目之后执行插入操作(使用 INSERTAPPENDCOLLECT 执行)时,将在后续循环传递中处理新插入的行。

例如,如果您在第 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 条件的行。 当然,您也可以通过在 VALUEREDUCE 表达式中使用 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 实现删除

假设有一个内表为:

image.png

那么使用又 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 那一行数据,运行结果为:

image.png

那么使用 FILTER 的方式就可以使用如下的方式,而不用定义 Flag 字段:

itab = FILTER #( itab WHERE a <> 2021 ).
cl_demo_output=>display( itab[] ).

删除前:

image.png

过滤删除后:

image.png

附:内表表达式 VALUE 赋值

内表表达式 itab[ ...] 不支持 sy-subrc。到目前为止,如果找不到方括号中指定的表格行,就会出现异常。并不是每个人都喜欢这种做法。

作为一种变通方法,可以将表格表达式放在包含 OPTIONALDEFAULT 附加信息的 VALUEREF 表达式中。如果找不到行,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 ).

参考链接: