You know the GROUP BY clause from SQL. There was not such a clause for internal tables up to now. All we had was that clumsy group level processing with statements AT NEW ... that relied on the order of table columns and contents that is sorted respectively.
With release 7.40, SP08 there is a real GROUP BY clause for LOOP AT itab that is much more powerful than the SQL one.
DATA flights TYPE TABLE OF spfli WITH EMPTY KEY.
SELECT * FROM spfli
WHERE carrid = '...'
INTO TABLE @flights.
DATA members LIKE flights.
LOOP AT flights INTO DATA(flight)
GROUP BY ( carrier = flight-carrid cityfr = flight-cityfrom )
ASCENDING
ASSIGNING FIELD-SYMBOL(<group>).
CLEAR members.
LOOP AT GROUP <group> ASSIGNING FIELD-SYMBOL(<flight>).
members = VALUE #( BASE members ( <flight> ) ).
ENDLOOP.
cl_demo_output=>write( members ).
ENDLOOP.
cl_demo_output=>display( ).
Looks like dreaded nested LOOPs, but it isn't quite that - no quadratic behavior! What happens here is that the first LOOP statement is executed over all internal table lines in one go and the new GROUP BY addition groups the lines. Technically, the lines are bound internally to a group that belongs to a group key that is specified behind GROUP BY.The group key is calculated for each loop pass. And the best is, it need not be as simple as using only column values, but you can use any expressions here that normally depend on the contents of the current line, e.g. comparisons, method calls, .... The LOOP body is not evaluated in this phase!
Only after the grouping phase, the LOOP body is evaluated. Now a second (but not nested) loop is carried out over the groups constructed in the first phase. Inside this group loop you can access the group using e.g. the field symbol <group> that is assigned to the group in the above example. If you want to access the members of the group, you can us the new LOOP AT GROUP statement, which enables a member loop within the group loop. In the example, the members are inserted into a member table and displayed.
Here another example, where the group key is evaluated from method calls:
LOOP AT flights INTO DATA(wa)
GROUP BY ( tz_from = get_time_zone( wa-airpfrom )
tz_to = get_time_zone( wa-airpto ) )
ASSIGNING FIELD-SYMBOL(<group>).
...
ENDLOOP.
Of course, there is also expression enabled syntax for grouping internal tables.
In a first step, we get rid of LOOP AT GROUP by replacing it with a FOR expression:
DATA members LIKE flights.
LOOP AT flights INTO DATA(flight)
GROUP BY ( carrier = flight-carrid cityfr = flight-cityfrom )
ASCENDING
ASSIGNING FIELD-SYMBOL(<group>).
members = VALUE #( FOR m IN GROUP <group> ( m ) ).
cl_demo_output=>write( members ).
ENDLOOP.
cl_demo_output=>display( ).
The IN GROUP is a new addition to FOR. Second, away with the outer LOOP:
TYPES t_flights LIKE flights.
DATA out TYPE REF TO if_demo_output.
out = REDUCE #( INIT o = cl_demo_output=>new( )
FOR GROUPS <group> OF flight IN flights
GROUP BY ( carrier = flight-carrid cityfr = flight-cityfrom )
ASCENDING
LET members = VALUE t_flights( FOR m IN GROUP <group> ( m ) ) IN
NEXT o = o->write( members ) ).
out->display( ).
FOR GROUPS is another new FOR variant. Believe me, it does the same as the variants above. But for reasons of readability, the combination of LOOP AT GROUP with a FOR IN GROUP within might be the preferable one, at least for this example :twisted: .
For a deep dive, read this and this!
PS: If you are confused what kind of loops and nesting takes place, have a look at my comment below.
PPS: For a step-by-step approach to GROUP BY see GROUP BY for Internal Tables - Step by Step
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
2 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 |