Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Using Loop At ... Group by with data references

0 Kudos

Dear Community,

i have some problems in using Loop at ref_itab->* Group by in a two level approach with data refernces since i would like to change content of the itab within both levels of the Group by.

The basics of the "Group by" are clear, but i have problems with (or better i have no clue about) keeping the reference to the data lines so the changes are transfered all the way back into the internal table.

Let me try to explain it in an dummy-excample.

DATA(ref_sflight_tab) = NEW tpda_sflight_tab( ).
DATA: ld_prices_changed         TYPE abap_bool VALUE abap_false,
      ld_not_all_prices_changed TYPE abap_bool VALUE abap_false.

SELECT *  FROM sflight INTO TABLE ref_sflight_tab->*.

LOOP AT ref_sflight_tab->* INTO DATA(ls_sflight_level1)
  GROUP BY ( carrid = ls_sflight_level1-carrid
             connid = ls_sflight_level1-connid
             size_level1 = GROUP SIZE )
  REFERENCE INTO DATA(ref_sflight_tab_group_level1).
  "reset checker
  ld_not_all_prices_changed = abap_false.

* now level2 Group
  LOOP AT GROUP ref_sflight_tab_group_level1 INTO DATA(ls_sflight_level2)
    GROUP BY ( fldate =  ls_sflight_level2-fldate
               size_level2 = GROUP SIZE )
    REFERENCE INTO DATA(ref_sflight_tab_group_level2).


*   --> I need a reference table with the lines of the level2 group members that are basically 
*       the lines of the ref_sflight_tab->*
*   Goal: if I hand that subset of records over to the method below I can change the prices of 
*         of this specific lines of ref_sflight_tab->* (basically of Level 0)
*   the Method has a boolean returning Parameter and a changing Data Ref Parameter (ref to tpda_sflight_tab)
    
    ld_prices_changed = lcl_some_class=>check_and_change_price_by_date(
                                          CHANGING crt_sflight_tab = ref_sflight_tab_group_level2_members_tab ).
    IF ld_prices_changed = abap_false.
      ld_not_all_prices_changed = abap_true.
    ENDIF.

  ENDLOOP. "Grouping of level 2

  IF ld_not_all_prices_changed = abap_false.

*  --> I need a reference table with the lines of the level1 group members that are again 
*      the lines of the ref_sflight_tab->*
*      and also contains the changed prices from the changes within the Level2 loop.
* Goal: I need to handle these Level 1 Members in case all prices changed. In other cases I will do something else or nothing.
*       Lets assume I need to send out an eMail with the details of this connection-ID to the Carrier
*       and in case email was send I add a new status to each data record and link the email to each 
*       record using GOS

    lcl_some_class=>send_email_wth_details_to_carr( CHANGING crt_sflight_tab = ref_sflight_tab_group_level1_members_tab ).

  ENDIF.
ENDLOOP.
Assume this was also just a called method.

So thanks to the data reference passed to this method, the changes arrived outside this method an can be saved to a database or can be used otherwise.

Can anyone help me out?

Kind regards
Markus

6 REPLIES 6

Sandra_Rossi
Active Contributor
0 Kudos

I'm quite sure it's possible but I don't get exactly your question, so it would be nice if you could provide an example with input data and expected result, (if possible as testable ABAP code) it would be more easy to understand.

0 Kudos

I can't run this code since i miss the needed details, so no garantie for misstypes, but it could be like this:
I hope it helps.

*&---------------------------------------------------------------------*
*& Report  ztest_some_loop_group_02
*&
*&---------------------------------------------------------------------*
*&
*&  Test of Loop with Group By
*&
*&---------------------------------------------------------------------*
REPORT ztest_some_loop_group_02.


CLASS sample DEFINITION.
  PUBLIC SECTION.
    TYPES BEGIN OF mtys_changed_flight.
    INCLUDE TYPE sflight.
    TYPES new_price TYPE s_price.
    TYPES status TYPE char10.
    TYPES END OF mtys_changed_flight.
    TYPES mtyt_changed_flights TYPE STANDARD TABLE OF mtys_changed_flight WITH EMPTY KEY.

    CLASS-METHODS main.
  PRIVATE SECTION.

    CLASS-METHODS process_flights
      CHANGING crt_sflights TYPE REF TO mtyt_changed_flights.

    CLASS-METHODS check_and_change_price_by_date
      CHANGING  crt_sflights                 TYPE REF TO mtyt_changed_flights
      RETURNING VALUE(rt_prices_has_changed) TYPE abap_bool.

    CLASS-METHODS set_status_at_carrid_connid
      CHANGING crt_sflights TYPE REF TO mtyt_changed_flights.

ENDCLASS.

CLASS sample IMPLEMENTATION.
  METHOD main.

    DATA(lrt_sflights) = NEW mtyt_changed_flights( ).

    SELECT * FROM sflight INTO CORRESPONDING FIELDS OF TABLE @lrt_sflights->* UP TO 200 ROWS.

    cl_demo_output=>display( lrt_sflights->* ).

    sample=>process_flights( CHANGING  crt_sflights = lrt_sflights ).

    "expected result: I see the changes that where made is method process_flights
    cl_demo_output=>display( lrt_sflights->* ).

  ENDMETHOD.


  METHOD process_flights.

    DATA: ld_prices_changed         TYPE abap_bool VALUE abap_false,
          ld_not_all_prices_changed TYPE abap_bool VALUE abap_false.


    LOOP AT crt_sflights->* INTO DATA(ls_sflight_level1)
      GROUP BY ( carrid = ls_sflight_level1-carrid
                 connid = ls_sflight_level1-connid
                 size_level1 = GROUP SIZE )
      REFERENCE INTO DATA(ref_sflight_tab_group_level1).
      "reset checker
      ld_not_all_prices_changed = abap_false.

* now level2 Group
      LOOP AT GROUP ref_sflight_tab_group_level1 INTO DATA(ls_sflight_level2)
        GROUP BY ( fldate =  ls_sflight_level2-fldate
                   size_level2 = GROUP SIZE )
        REFERENCE INTO DATA(ref_sflight_tab_group_level2).


*   --> I need a reference table with the lines of the level2 group members that are basically
*       the lines of the ref_sflight_tab->*
* Goal: if I hand that subset of records over to the method below I can change the prices of
*       of this specific lines of ref_sflight_tab->* (basically of Level 0)

* "sflights_group_level2_members" is just the placeholder for the table reference I haven'd figured out yet
        ld_prices_changed = sample=>check_and_change_price_by_date(
                                              CHANGING crt_sflights = sflights_group_level2_members ).
        IF ld_prices_changed = abap_false.
          ld_not_all_prices_changed = abap_true.
        ENDIF.

      ENDLOOP. "Grouping of level 2


      IF ld_not_all_prices_changed = abap_false.

*   --> I need a reference table with the lines of the level1 group members that are again
*       the lines of the ref_sflight_tab->*
*       and also contains the changed prices from the changes within the Level2 loop.
* Goal: I need to handle these Level 1 Members in case all prices for all dates has changed.

* "sflights_group_level1_members" is just the placeholder for the table reference I haven'd figured out yet
        sample=>set_status_at_carrid_connid( CHANGING crt_sflights = sflights_group_level1_members ).

      ENDIF.

    ENDLOOP.


  ENDMETHOD.



  METHOD check_and_change_price_by_date.
*   short dummy code to provide some changes
    rt_prices_has_changed = abap_false.

    LOOP AT crt_sflights->* REFERENCE INTO DATA(lrs_sflight)
      WHERE fldate BETWEEN 20210101 AND 20213112.

      lrs_sflight->new_price = lrs_sflight->price - 50.
      rt_prices_has_changed = abap_true.
    ENDLOOP.


  ENDMETHOD.

  METHOD set_status_at_carrid_connid.
* also here just some dummy change for Level 1 members
* please do not question if this would make sense or not ;-).
    LOOP AT crt_sflights->* REFERENCE INTO DATA(lrs_sflight).
      lrs_sflight->status = 'CHANGED'.
    ENDLOOP.

  ENDMETHOD.

ENDCLASS.

START-OF-SELECTION.
  sample=>main( ).

Sandra_Rossi
Active Contributor
0 Kudos

I meant something more like below. I change the internal table through data references, with clear input data and expected result (run using Ctrl+Shift+F10):

REPORT.
CLASS ltc_main DEFINITION
      FOR TESTING
      DURATION SHORT
      RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    METHODS test FOR TESTING.
ENDCLASS.
CLASS ltc_main IMPLEMENTATION.
  METHOD test.
    TYPES: BEGIN OF ty_group_level1_line,
             ref_to_sflight_line TYPE REF TO sflight,
           END OF ty_group_level1_line,
           ty_sflight_tab TYPE STANDARD TABLE OF sflight WITH DEFAULT KEY.
    DATA(sflight_tab) = VALUE ty_sflight_tab(
        ( carrid = 'LH' connid = '0417' )
        ( carrid = 'LH' connid = '0417' ) ).
    LOOP AT sflight_tab REFERENCE INTO DATA(ref_sflight)
          GROUP BY ( carrid = ref_sflight->carrid
                     connid = ref_sflight->connid
                     size_level1 = GROUP SIZE )
          REFERENCE INTO DATA(ref_sflight_tab_group_level1).
      LOOP AT GROUP ref_sflight_tab_group_level1 REFERENCE INTO DATA(ref_sflight_level1)
        GROUP BY ( fldate =  ref_sflight_level1->fldate
                   size_level2 = GROUP SIZE )
        REFERENCE INTO DATA(ref_sflight_tab_group_level2).
        LOOP AT GROUP ref_sflight_tab_group_level2 REFERENCE INTO DATA(ref_sflight_level2).
          ref_sflight_level2->carrid = 'AA'.
        ENDLOOP.
      ENDLOOP.
    ENDLOOP.
    cl_abap_unit_assert=>assert_equals( act = sflight_tab exp = VALUE ty_sflight_tab(
        ( carrid = 'AA' connid = '0417' )
        ( carrid = 'AA' connid = '0417' ) ) ).
  ENDMETHOD.

0 Kudos

Hm, sorry I'm not familiar with writing such a test class. Honestly, I never did that before.

But you understood the case correctly, just one little detail is not correct.

In your code, you changed the records during the Loop At Group:

        LOOP AT GROUP ref_sflight_tab_group_level2 REFERENCE INTO DATA(ref_sflight_level2).
          ref_sflight_level2->carrid = 'AA'.
        ENDLOOP.

But that’s not what I planned to do. Instead I would like to pass the Reference to the whole Group Members to a method which then deals with the checks and changes.

I would like to separate and split functionality in different methods and not process everything within the Loop at Group.

touzik_itc
Active Participant
0 Kudos

If you create a new table with members like that

        DATA(sflights_group_level2_members) = 
          NEW mtyt_changed_flights( FOR <wa> IN crt_sflights->*
          WHERE ( carrid = ref_sflight_tab_group_level1->carrid AND
                  connid = ref_sflight_tab_group_level1->connid AND
                  fldate = ref_sflight_tab_group_level2->fldate )
                ( <wa> ) ).

check_and_change_price_by_date will change this new table and not an original table.

You should pass a reference to the original table, CARRID, CONNID and FLDATE to check_and_change_price_by_date and add corresponding condition at the LOOP statement.

Sandra_Rossi
Active Contributor
0 Kudos

If you mean having this in your method:

LOOP AT GROUP group_as_importing_parameter ...
where the group is defined in the caller method,then I guess it's impossible as I don't see how the compiler could understand that.But probably you can write your code differently.