LOOP itab vs VALUE FOR 过滤,哪个更高效?

LOOP itab vs VALUE FOR filtering, which is more efficient?

我正在尝试为以下场景编写 for 循环语句:

我使用 select 将多个 tvarvc 条目数据输入 T_TVARVC

LOOP AT t_tvarvc INTO DATA(s_tvarvc).
    CASE s_tvarvc-name.
      WHEN c_augru.
        s_tvarvc_range = CORRESPONDING #( s_tvarvc ).
        APPEND s_tvarvc_range TO t_augru.

      WHEN c_vkorg.
        s_tvarvc_range = CORRESPONDING #( s_tvarvc ).
        APPEND s_tvarvc_range TO t_vkorg.
    ENDCASE.
ENDLOOP. 

这是我想出的:

DATA(t_augru) = VALUE tt_tvarvc( FOR s_tvarvc IN t_tvarvc
                                  WHERE ( name = c_augru )
                                  ( CORRESPONDING #( s_tvarvc ) ) ).

DATA(t_vkorg) = VALUE tt_tvarvc( FOR s_tvarvc IN t_tvarvc
                                  WHERE ( name = c_vkorg )
                                  ( CORRESPONDING #( s_tvarvc ) ) ). 

我的观察是,通过使用 LOOP ATCASE 语句组合,迭代次数将与 T_TVARVC 中的条目数相同。
但是,当对每个范围 table 使用 FOR 循环时,必须遍历 T_TVARVC 多次才能到达所需的条目,从而导致比第一种情况更多的迭代。

这可以用更有效的方式写吗?

我同意你关于加倍迭代的观察,为了让它更快,我认为唯一的解决方案是只使用一个循环,考虑到内部 table 尚未排序,这限制了可能的解决方案很多,我得出这个解决方案:

TYPES: tt_tvarvc TYPE STANDARD TABLE OF tvarvc WITH EMPTY KEY,
       BEGIN OF ty_ranges,
         t_augru TYPE tt_tvarvc,
         t_vkorg TYPE tt_tvarvc,
       END OF ty_ranges.
CONSTANTS: c_augru TYPE tvarvc-name VALUE 'AUGRU',
           c_vkorg TYPE tvarvc-name VALUE 'VKORG'.

DATA(t_tvarvc) = VALUE tt_tvarvc( for i = 1 while i <= 100 ( name = c_augru ) 
                                                           ( name = c_vkorg ) ).
DATA(ranges) = REDUCE ty_ranges(
                  INIT ranges2 = VALUE ty_ranges( )
                  FOR <tvarv> IN t_tvarvC
                  NEXT ranges2-t_augru = COND #( WHEN <tvarv>-name = c_augru
                                              THEN VALUE #( BASE ranges2-t_augru ( <tvarv> ) )
                                              ELSE ranges2-t_augru )
                       ranges2-t_vkorg = COND #( WHEN <tvarv>-name = c_vkorg
                                              THEN VALUE #( BASE ranges2-t_vkorg ( <tvarv> ) )
                                              ELSE ranges2-t_vkorg ) ).

(您将在代码中使用 ranges-t_augruranges-t_vkorg 而不是 t_augrut_vkorg

您可以立即看到代码比您的两个代码段中的任何一个都更不清晰。

此外,与您的经典循环相比,性能没有任何提升。

回到你的有两次 FOR 迭代的代码片段,我们可以看到与经典循环相比,目标非常明确(我的意见)。它当然更慢,但可能你不需要获得几微秒,所以我认为这是最好的解决方案(仍然是我的意见)。

只是添加与该部分相关的另一个答案

My observation is that, by using LOOP AT and CASE statement combo, the number of iterations will be same as the number of entries in T_TVARVC.
But when using a FOR loop for each range table, T_TVARVC has to be traversed more times to reach the desired entry thus causing multiple iterations more than the first scenario.

只有当您没有相关字段的排序索引时,才会出现这种情况。假设您使用 USING KEY sk_name WHERE ( object = c_vkorg ) 而不是 WHERE ( name = c_vkorg )。这将知道您正在搜索的值在 log n 时间开始的索引。然后它将只处理与键匹配的行,从不循环处理其他任何内容。
这可能会节省大量时间。

Index Val1 Val2 Val3 (sorted index)
1 A 9999 AAA
2 B 1213 AAB
3 C 554 AAC
... ... ... ...
500 X 1 AUGUR <-- Starting here with loop

缺点是排序的辅助键也需要时间来构建(和一些内存)。如果您有其他代码也需要快速访问,这可能不是什么问题。
辅助键是惰性的,所以第一次使用时会创建它们。

在您的场景中,您必须决定什么是值得的。是否有频繁的读取访问需要密钥?几行?建立密钥访问权限是否更昂贵,因为其他地方不需要密钥?二级密钥多久会失效等等等等

(注意:如果取消注释 xsdbool,则从测量中排除构建辅助密钥所需的时间)。

REPORT ztest.

START-OF-SELECTION.
  PERFORM standard.
  PERFORM sorted_secondary.
  PERFORM sorted_secondary_val.

FORM standard.
  DATA t_tadir TYPE STANDARD TABLE OF tadir WITH EMPTY KEY.
  DATA t_clas TYPE STANDARD TABLE OF tadir-obj_name WITH EMPTY KEY.
  DATA t_tran TYPE STANDARD TABLE OF tadir-obj_name WITH EMPTY KEY.

  SELECT * FROM tadir UP TO 1000000 ROWS INTO TABLE @t_tadir ORDER BY PRIMARY KEY.
*  DATA(dummy) = xsdbool( line_exists( t_tadir[ key primary_key object = 'CLAS' ] ) ).

  GET RUN TIME FIELD DATA(t1).
  LOOP AT t_tadir ASSIGNING FIELD-SYMBOL(<s_tadir>).
    CASE <s_tadir>-object.
      WHEN 'CLAS'.
        APPEND <s_tadir>-obj_name TO t_clas.

      WHEN 'TRAN'.
        APPEND <s_tadir>-obj_name TO t_tran.
    ENDCASE.
  ENDLOOP.
  GET RUN TIME FIELD DATA(t2).
  WRITE: |{ ( t2 - t1 ) / '1000.0' / '1000.0' }, { lines( t_tadir ) }, { lines( t_clas ) }, { lines( t_tran ) }|.
  NEW-LINE.
ENDFORM.

FORM sorted_secondary.
  DATA t_tadir TYPE STANDARD TABLE OF tadir WITH NON-UNIQUE SORTED KEY sk_object COMPONENTS object.
  DATA t_clas TYPE STANDARD TABLE OF tadir-obj_name WITH EMPTY KEY.
  DATA t_tran TYPE STANDARD TABLE OF tadir-obj_name WITH EMPTY KEY.

  SELECT * FROM tadir UP TO 1000000 ROWS INTO TABLE @t_tadir ORDER BY PRIMARY KEY.
*  DATA(dummy) = xsdbool( line_exists( t_tadir[ key sk_object object = 'CLAS' ] ) ).

  GET RUN TIME FIELD DATA(t1).
  LOOP AT t_tadir ASSIGNING FIELD-SYMBOL(<s_tadir>) USING KEY sk_object WHERE object = 'CLAS'.
    APPEND <s_tadir>-obj_name TO t_clas.
  ENDLOOP.
  LOOP AT t_tadir ASSIGNING <s_tadir> USING KEY sk_object WHERE object = 'TRAN'.
    APPEND <s_tadir>-obj_name TO t_tran.
  ENDLOOP.
  GET RUN TIME FIELD DATA(t2).
  WRITE: |{ ( t2 - t1 ) / '1000.0' / '1000.0' }, { lines( t_tadir ) }, { lines( t_clas ) }, { lines( t_tran ) }|.
  NEW-LINE.
ENDFORM.

FORM sorted_secondary_val.
  DATA t_tadir TYPE STANDARD TABLE OF tadir WITH NON-UNIQUE SORTED KEY sk_object COMPONENTS object.
  DATA t_clas TYPE STANDARD TABLE OF tadir-obj_name WITH EMPTY KEY.
  DATA t_tran TYPE STANDARD TABLE OF tadir-obj_name WITH EMPTY KEY.

  SELECT * FROM tadir UP TO 1000000 ROWS INTO TABLE @t_tadir ORDER BY PRIMARY KEY.
*  DATA(dummy) = xsdbool( line_exists( t_tadir[ key sk_object object = 'CLAS' ] ) ).

  GET RUN TIME FIELD DATA(t1).
  t_clas = VALUE #( for <fs> in t_tadir USING KEY sk_object WHERE ( object = 'CLAS' ) ( <fs>-obj_name ) ).
  t_tran = VALUE #( for <fs> in t_tadir USING KEY sk_object WHERE ( object = 'TRAN' ) ( <fs>-obj_name ) ).
  GET RUN TIME FIELD DATA(t2).
  WRITE: |{ ( t2 - t1 ) / '1000.0' / '1000.0' }, { lines( t_tadir ) }, { lines( t_clas ) }, { lines( t_tran ) }|.
  NEW-LINE.
ENDFORM.

另外:LOOP AT ... ASSIGNING/REFERENCE INTO 可能比 LOOP AT ... INTO 快。由于您没有进行不应该反映在原始数据源中的写访问,因此没有理由在每个循环步骤中复制每一行。

您也可以尝试专门的 FILTER 语句来实现同样的效果:

DATA lt_tadir TYPE SORTED TABLE OF tadir WITH NON-UNIQUE KEY object. 

SELECT * FROM tadir UP TO 1000000 ROWS INTO TABLE lt_tadir ORDER BY PRIMARY KEY. 

DATA(clas) = FILTER #( lt_tadir USING KEY primary_key WHERE object = 'CLAS' ). 
DATA(trans) = FILTER #( lt_tadir USING KEY primary_key WHERE object = 'TRAN' ).

我采用了@peterulb 为 1M table 准备的片段,这是我的测量值:

0.032947, 1000000, 128776, 0 <- FILTER
0.139579, 1000000, 128776, 0 <- standard
0.239092, 1000000, 128776, 0 <- sorted_secondary
0.242161, 1000000, 128776, 0 <- sorted_secondary_val

尽管 FILTER 使用内联声明并将 所有 源字段传输到结果 table (这是设计使然),但它的执行速度比其他变体。

我不坚持这将是所有数据库的经验法则,但至少 HANADB 上的 ABAP 7.54 对 FILTER 语句使用了一些内置优化。