Der REDUCE-Ausdruck ermöglicht das Aggregieren und Akkumulieren von Werten über eine Iteration. Er ist vergleichbar mit fold oder reduce aus funktionalen Programmiersprachen und ersetzt viele LOOP-Konstrukte mit Akkumulator-Variablen.
Syntax
REDUCE <typ>( INIT <akkumulator1> = <startwert1> [ <akkumulator2> = <startwert2> ... ] FOR <variable> IN <tabelle> [ WHERE ( <bedingung> ) ] [ FOR <variable2> ... ] NEXT <akkumulator1> = <ausdruck1> [ <akkumulator2> = <ausdruck2> ... ])Grundprinzip
- INIT: Initialisiert einen oder mehrere Akkumulatoren
- FOR: Iteriert über eine Tabelle (oder mehrere)
- NEXT: Aktualisiert die Akkumulatoren pro Iteration
- Das Ergebnis ist der Wert des ersten Akkumulators nach der letzten Iteration
Beispiele
1. Einfache Summe berechnen
DATA: lt_numbers TYPE TABLE OF i.
lt_numbers = VALUE #( ( 10 ) ( 20 ) ( 30 ) ( 40 ) ( 50 ) ).
" Klassisch mit LOOPDATA: lv_sum TYPE i.LOOP AT lt_numbers INTO DATA(lv_num). lv_sum = lv_sum + lv_num.ENDLOOP.
" Modern mit REDUCEDATA(lv_sum2) = REDUCE i( INIT sum = 0 FOR num IN lt_numbers NEXT sum = sum + num).
WRITE: / 'Summe:', lv_sum2. " 1502. Summe aus Strukturtabelle
TYPES: BEGIN OF ty_order, order_id TYPE i, amount TYPE p DECIMALS 2, END OF ty_order.
DATA: lt_orders TYPE TABLE OF ty_order.
lt_orders = VALUE #( ( order_id = 1 amount = '100.50' ) ( order_id = 2 amount = '200.00' ) ( order_id = 3 amount = '150.75' )).
" Gesamtbetrag berechnenDATA(lv_total) = REDUCE p DECIMALS 2( INIT total = CONV p DECIMALS 2( 0 ) FOR ls_order IN lt_orders NEXT total = total + ls_order-amount).
WRITE: / 'Gesamtbetrag:', lv_total. " 451.253. Anzahl zählen mit WHERE
TYPES: BEGIN OF ty_product, id TYPE i, category TYPE string, active TYPE abap_bool, END OF ty_product.
DATA: lt_products TYPE TABLE OF ty_product.
lt_products = VALUE #( ( id = 1 category = 'A' active = abap_true ) ( id = 2 category = 'B' active = abap_false ) ( id = 3 category = 'A' active = abap_true ) ( id = 4 category = 'A' active = abap_false )).
" Anzahl aktiver Produkte der Kategorie ADATA(lv_count) = REDUCE i( INIT count = 0 FOR ls_prod IN lt_products WHERE ( category = 'A' AND active = abap_true ) NEXT count = count + 1).
WRITE: / 'Anzahl:', lv_count. " 24. Maximum finden
DATA: lt_values TYPE TABLE OF i.
lt_values = VALUE #( ( 42 ) ( 17 ) ( 99 ) ( 8 ) ( 73 ) ).
" Maximum findenDATA(lv_max) = REDUCE i( INIT max = 0 FOR val IN lt_values NEXT max = nmax( val1 = max val2 = val )).
WRITE: / 'Maximum:', lv_max. " 995. Minimum finden
" Minimum finden (mit erstem Wert als Startwert)DATA(lv_min) = REDUCE i( INIT min = lt_values[ 1 ] FOR val IN lt_values NEXT min = nmin( val1 = min val2 = val )).
WRITE: / 'Minimum:', lv_min. " 86. Strings verketten
DATA: lt_names TYPE TABLE OF string.
lt_names = VALUE #( ( `Anna` ) ( `Bernd` ) ( `Clara` ) ).
" Namen mit Komma verkettenDATA(lv_concat) = REDUCE string( INIT result = `` FOR name IN lt_names NEXT result = COND #( WHEN result IS INITIAL THEN name ELSE result && `, ` && name )).
WRITE: / lv_concat. " Anna, Bernd, Clara7. Mehrere Akkumulatoren
TYPES: BEGIN OF ty_stats, sum TYPE i, count TYPE i, END OF ty_stats.
DATA: lt_numbers TYPE TABLE OF i.
lt_numbers = VALUE #( ( 10 ) ( 20 ) ( 30 ) ( 40 ) ).
" Summe und Anzahl gleichzeitig berechnenDATA(ls_stats) = REDUCE ty_stats( INIT sum = 0 count = 0 FOR num IN lt_numbers NEXT sum = sum + num count = count + 1).
DATA(lv_average) = ls_stats-sum / ls_stats-count.
WRITE: / 'Summe:', ls_stats-sum. " 100WRITE: / 'Anzahl:', ls_stats-count. " 4WRITE: / 'Durchschnitt:', lv_average. " 258. Tabelle aus Tabelle aufbauen
TYPES: ty_names TYPE TABLE OF string WITH EMPTY KEY.
DATA: lt_persons TYPE TABLE OF ty_person.
lt_persons = VALUE #( ( name = 'Max' age = 30 ) ( name = 'Anna' age = 25 ) ( name = 'Peter' age = 35 )).
" Namen extrahieren in neue TabelleDATA(lt_names) = REDUCE ty_names( INIT names = VALUE ty_names( ) FOR ls_person IN lt_persons NEXT names = VALUE #( BASE names ( ls_person-name ) )).
" Ergebnis: Max, Anna, Peter9. Filtern und Aggregieren kombiniert
TYPES: BEGIN OF ty_sale, region TYPE string, amount TYPE p DECIMALS 2, END OF ty_sale.
DATA: lt_sales TYPE TABLE OF ty_sale.
lt_sales = VALUE #( ( region = 'NORTH' amount = '1000.00' ) ( region = 'SOUTH' amount = '2000.00' ) ( region = 'NORTH' amount = '1500.00' ) ( region = 'EAST' amount = '800.00' )).
" Summe nur für Region NORTHDATA(lv_north_total) = REDUCE p DECIMALS 2( INIT total = CONV p DECIMALS 2( 0 ) FOR ls_sale IN lt_sales WHERE ( region = 'NORTH' ) NEXT total = total + ls_sale-amount).
WRITE: / 'NORTH Umsatz:', lv_north_total. " 2500.0010. Verschachtelte Iteration (zwei FOR)
TYPES: BEGIN OF ty_category, name TYPE string, items TYPE TABLE OF i WITH EMPTY KEY, END OF ty_category.
DATA: lt_categories TYPE TABLE OF ty_category.
lt_categories = VALUE #( ( name = 'A' items = VALUE #( ( 10 ) ( 20 ) ) ) ( name = 'B' items = VALUE #( ( 30 ) ( 40 ) ( 50 ) ) )).
" Summe aller Items über alle KategorienDATA(lv_total_all) = REDUCE i( INIT total = 0 FOR ls_cat IN lt_categories FOR lv_item IN ls_cat-items NEXT total = total + lv_item).
WRITE: / 'Gesamtsumme:', lv_total_all. " 15011. FOR mit UNTIL/WHILE
" Iteration mit Bedingung (nicht über Tabelle)DATA(lv_factorial) = REDUCE i( INIT fact = 1 n = 1 UNTIL n > 5 NEXT fact = fact * n n = n + 1).
WRITE: / '5! =', lv_factorial. " 120
" Mit WHILEDATA(lv_sum_while) = REDUCE i( INIT sum = 0 i = 1 WHILE i <= 10 NEXT sum = sum + i i = i + 1).
WRITE: / 'Summe 1-10:', lv_sum_while. " 5512. LET für lokale Hilfsvariablen
TYPES: BEGIN OF ty_item, quantity TYPE i, price TYPE p DECIMALS 2, END OF ty_item.
DATA: lt_items TYPE TABLE OF ty_item.
lt_items = VALUE #( ( quantity = 5 price = '10.00' ) ( quantity = 3 price = '25.00' ) ( quantity = 10 price = '5.00' )).
" Gesamtwert berechnen (Menge * Preis)DATA(lv_total_value) = REDUCE p DECIMALS 2( INIT total = CONV p DECIMALS 2( 0 ) FOR ls_item IN lt_items LET line_total = ls_item-quantity * ls_item-price IN NEXT total = total + line_total).
WRITE: / 'Gesamtwert:', lv_total_value. " 175.0013. Gruppierung mit REDUCE
TYPES: BEGIN OF ty_group_result, category TYPE string, sum TYPE i, END OF ty_group_result, ty_group_results TYPE TABLE OF ty_group_result WITH EMPTY KEY.
DATA: lt_items TYPE TABLE OF ty_product.
lt_items = VALUE #( ( id = 1 category = 'A' active = abap_true ) ( id = 2 category = 'B' active = abap_true ) ( id = 3 category = 'A' active = abap_true ) ( id = 4 category = 'A' active = abap_false ) ( id = 5 category = 'B' active = abap_true )).
" Anzahl pro Kategorie zählenDATA(lt_by_category) = REDUCE ty_group_results( INIT result = VALUE ty_group_results( ) FOR ls_item IN lt_items FOR GROUPS <group> OF <item> IN lt_items GROUP BY <item>-category NEXT result = VALUE #( BASE result ( category = <group> sum = REDUCE i( INIT cnt = 0 FOR m IN GROUP <group> NEXT cnt = cnt + 1 ) ) )).14. Boolean-Aggregation (ANY/ALL)
" Prüfen, ob MINDESTENS EIN Element die Bedingung erfüllt (ANY)DATA(lv_any_active) = REDUCE abap_bool( INIT any = abap_false FOR ls_prod IN lt_products NEXT any = xsdbool( any = abap_true OR ls_prod-active = abap_true )).
" Prüfen, ob ALLE Elemente die Bedingung erfüllen (ALL)DATA(lv_all_active) = REDUCE abap_bool( INIT all = abap_true FOR ls_prod IN lt_products NEXT all = xsdbool( all = abap_true AND ls_prod-active = abap_true )).
IF lv_any_active = abap_true. WRITE: / 'Mindestens ein Produkt ist aktiv'.ENDIF.
IF lv_all_active = abap_true. WRITE: / 'Alle Produkte sind aktiv'.ELSE. WRITE: / 'Nicht alle Produkte sind aktiv'.ENDIF.15. REDUCE in Methodenaufrufen
METHODS: display_total IMPORTING iv_total TYPE p.
" Direkt als Parameterdisplay_total( iv_total = REDUCE p DECIMALS 2( INIT sum = CONV p DECIMALS 2( 0 ) FOR ls_order IN lt_orders NEXT sum = sum + ls_order-amount )).Vergleich: REDUCE vs. LOOP
" === KLASSISCH MIT LOOP ===DATA: lv_sum TYPE i, lv_count TYPE i, lv_max TYPE i.
LOOP AT lt_numbers INTO DATA(lv_num). lv_sum = lv_sum + lv_num. lv_count = lv_count + 1. IF lv_num > lv_max. lv_max = lv_num. ENDIF.ENDLOOP.
" === MODERN MIT REDUCE ===DATA(ls_result) = REDUCE ty_result( INIT sum = 0 count = 0 max = 0 FOR num IN lt_numbers NEXT sum = sum + num count = count + 1 max = nmax( val1 = max val2 = num )).Wichtige Hinweise / Best Practice
- Der erste Akkumulator bestimmt den Rückgabetyp von
REDUCE. - Für mehrere Ergebnisse: Verwenden Sie einen Struktur-Akkumulator oder mehrere Akkumulatoren.
FOR ... INiteriert über Tabellen,FOR ... UNTIL/WHILEfür bedingte Iteration.WHEREfiltert die Iteration – effizienter alsIFimNEXT.LETermöglicht lokale Hilfsvariablen innerhalb der Iteration.- Verschachtelte
FOR-Schleifen sind möglich für mehrdimensionale Daten. BASEinVALUE #()erhält bestehende Tabelleneinträge beim Aufbau.- Kombinieren Sie mit
VALUE,CONDundFILTER. REDUCEist funktional – keine Seiteneffekte auf externe Variablen.- Bei komplexer Logik kann ein
LOOP ATlesbarer sein.