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 AT
和 CASE
语句组合,迭代次数将与 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_augru
和 ranges-t_vkorg
而不是 t_augru
和 t_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 语句使用了一些内置优化。
我正在尝试为以下场景编写 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 AT
和 CASE
语句组合,迭代次数将与 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_augru
和 ranges-t_vkorg
而不是 t_augru
和 t_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 语句使用了一些内置优化。