2

I'm looking for the equivalent of the AT LAST statement within the LOOP AT ... GROUP BY statement.

I'm looping using group by, and for performance reasons, I execute a call method every few groups, when a certain amount of records has been accumulated into an internal table. But I would like to add an extra condition to run this call at the end of the loop, if I'm in the last group.

Below I wrote a piece of executable code simulating my problem, I would like to process my last material before exiting the loop.

constants: processing_size_threshold type i value 10.
types: begin of ty_material,
         material_num type c length 3,
       end of ty_material.
data:  materials type standard table of ty_material,
       materials_bom type standard table of ty_material.

materials = value #( for i = 1 then i + 1 while i <= 3
                     ( material_num = conv #( i ) ) ).


loop at materials reference into data(material_grp) group by material_grp->material_num.

    loop at group material_grp reference into data(material).
      materials_bom = value #( base materials_bom (
                               lines of value #( for j = 1 then j + 1 while j <= 5
                                               ( material_num = |{ material->material_num }{ j }| ) ) ) ).
    endloop.

   "The 2nd condition of this IF is what I'm not able to figure out, I need material 3 BOM to be processed here
    if lines( materials_bom ) >= processing_size_threshold. "or group_num = last_group.
      "me->process_boms(materials_bom).
      clear materials_bom.
    endif.
endloop.
Suncatcher
  • 10,355
  • 10
  • 52
  • 90
RaTiO
  • 979
  • 2
  • 17
  • 33
  • 2
    Your example is usually solved and more easy to read by adding a condition after the outer loop: `if lines( materials_bom ) > 0. process_boms( materials_bom ). endif.` Is there any reason why you don't use this logic? – Sandra Rossi Jan 23 '21 at 09:07
  • 1
    Yes, that's what I did, but I didn't like the duplicated code. In my real case is a bit longer than in this example and I use parameters from the group to call the method. In any case, I asked this question out of curiosity since I already know a solution for my problem, but my question is about the loop. I thought that LOOP AT GROUP should allow a solution – RaTiO Jan 23 '21 at 19:35

2 Answers2

3

First: it is better to put real-world examples than just nonsensical tables filled with random numeric buzz like you did. You may just get the wrong answers that way.

If you want to process just the last group, you can use the GROUP INDEX for this:

SELECT matnr AS matno
  FROM vbap UP TO 100 ROWS
  INTO TABLE @DATA(materials)
  ORDER BY matnr.

TYPES: ty_mats LIKE materials.
DATA: bom LIKE materials.

DATA(uniques) = lines( VALUE ty_mats( FOR GROUPS value OF <line> IN materials GROUP BY ( matno = <line>-matno ) WITHOUT MEMBERS ( value ) ) ).
LOOP AT materials REFERENCE INTO DATA(refmat) GROUP BY ( id = refmat->matno size = GROUP SIZE gi = GROUP INDEX ) ASCENDING REFERENCE INTO DATA(group_ref).
  CHECK group_ref->*-gi = uniques.
  bom = VALUE ty_mats( BASE bom FOR <mat> IN GROUP group_ref ( matno = <mat>-matno ) ).
  me->process_boms( bom ). "<--here comes the drums
ENDLOOP.

Note: the table must be sorted for INDEX to show correct values.

In this snippet very simple approach is utilized: first we calculate the total number of unique groups and then check each group index whether it is the last one.

Note, that what is last in your dataset depends only on sort order, so you may easily end up with the inaccurate values for your requirement.

Suncatcher
  • 10,355
  • 10
  • 52
  • 90
  • Thanks that's a good solution. It involves extra processing for the "uniques" calculation but I guess there is no other way. If for some reason the table can't be sorted by INDEX, it's possible to just use a counter within the loop as well – RaTiO Jan 25 '21 at 14:00
  • Regarding using a real example I have to disagree. My system for instance is not an ERP so it doesn't have an VBAP table. My code snipped however can run in any ABAP 7.50+ and it's quite real, very close to my scenario. Maybe I could change the mocked up material numbers to real IDs or names, but I think it's much more useful if you can copy & paste the code without needing data from the underlying DB. Reproducible is also part of SO guidelines – RaTiO Jan 25 '21 at 14:03
  • In any system it's possible to pick some commonly-used table and construct example based on it, like `CRMD_ORDERADM_I` in CRM/SRM, or `ACDOCA` in S4HANA and so on – Suncatcher Jan 25 '21 at 14:37
  • @RatIo Use the counter inside loop was my first thought, but it will fail for filtered loop. `group_index` looks more straightforward. – astentx Jan 27 '21 at 07:23
0

did you mean this ?
sy-tabix current loop index may help here. Otherwise You may need to calculate the number of groups prior.

constants: processing_size_threshold type i value 10.
types: begin of ty_material,
         material_num type c length 3,
       end of ty_material.
data:  materials type standard table of ty_material,
       materials_bom type standard table of ty_material.

materials = value #( for i = 1 then i + 1 while i <= 3
                     ( material_num = conv #( i ) ) ).


loop at materials reference into data(material_grp) group by material_grp->material_num.

    loop at group material_grp reference into data(material).
      materials_bom = value #( base materials_bom (
                               lines of value #( for j = 1 then j + 1 while j <= 5
                                               ( material_num = |{ material->material_num }{ j }| ) ) ) ).
    endloop.

   "The 2nd condition of this IF is what I'm not able to figure out, I need material 3 BOM to be processed here
    if lines( materials_bom ) >= processing_size_threshold
    or lines( materials ) = sy-tabix.
      "me->process_boms(materials_bom).
      clear materials_bom.
    endif.
endloop.
phil soady
  • 11,043
  • 5
  • 50
  • 95
  • Hi Phil, I just needed to test it first before commenting, but sy-tabix doesn't have the group index, actually according to the documentation it should have the value that represents the line in the internal table for the group, but in my case I always see a zero. In any case, nothing guarantees that the last group corresponds to the last material, so this option can't be used – RaTiO Jan 25 '21 at 20:15