Sumowanie rekordów itab o tych samych polach.
Sumowanie rekordów itab o tych samych polach.
Mamy tabelę:
pernr type pernr_d,
skladnik type LGART,
kwota type MAXBT_SUM.
rekordy wyglądają następująco:
123 | 200 | 125
123 | 200 | 25
123 | 200 | 195
123 | 200 | 225
323 | 200 | 121
323 | 200 | 22
323 | 200 | 196
323 | 200 | 220
323 | 100 | 736
323 | 100 | 5
323 | 100 | 36
itd.
To, co chcę zrobić, to uzyskać sumę kwot dla każdego pracownika na każdym składniku, który u niego wystąpił, czyli:
123 | 200 | 570
323 | 200 | 559
323 | 100 | 777
Kręcenie pętlą i sumowanie nie wydaje mi się zbyt optymalne. Podobnie tworzenie jakiejś dodatkowej tabeli wynikowej.
Nie chce mi się wierzyć, że nie istnieje w ABAP jakaś instrukcja, która uporządkuje dane w sposób, na którym mi zależy.
Mogę prosić o pokierowanie?
pernr type pernr_d,
skladnik type LGART,
kwota type MAXBT_SUM.
rekordy wyglądają następująco:
123 | 200 | 125
123 | 200 | 25
123 | 200 | 195
123 | 200 | 225
323 | 200 | 121
323 | 200 | 22
323 | 200 | 196
323 | 200 | 220
323 | 100 | 736
323 | 100 | 5
323 | 100 | 36
itd.
To, co chcę zrobić, to uzyskać sumę kwot dla każdego pracownika na każdym składniku, który u niego wystąpił, czyli:
123 | 200 | 570
323 | 200 | 559
323 | 100 | 777
Kręcenie pętlą i sumowanie nie wydaje mi się zbyt optymalne. Podobnie tworzenie jakiejś dodatkowej tabeli wynikowej.
Nie chce mi się wierzyć, że nie istnieje w ABAP jakaś instrukcja, która uporządkuje dane w sposób, na którym mi zależy.
Mogę prosić o pokierowanie?
Re: Sumowanie rekordów itab o tych samych polach.
Samo kręcenie pętlą w pętli, nie, ale już z GROUP BY, tak. Tu mamy O(n logn)... do tego COLLECT...
Gotowy kod:
Gotowy kod:
Kod: Zaznacz cały
TYPES:
BEGIN OF ty_subsum,
pernr TYPE pernr_d,
skladnik TYPE lgart,
kwota TYPE maxbt_sum,
END OF ty_subsum,
tt_subsum TYPE SORTED TABLE OF ty_subsum WITH NON-UNIQUE KEY pernr skladnik.
DATA:
lt_subsum TYPE tt_subsum,
lt_subsum_aux TYPE tt_subsum.
lt_subsum = VALUE #( ( pernr = 123 skladnik = '0200' kwota = 125 )
( pernr = 123 skladnik = '0200' kwota = 25 )
( pernr = 123 skladnik = '0200' kwota = 195 )
( pernr = 123 skladnik = '0200' kwota = 225 )
( pernr = 323 skladnik = '0200' kwota = 121 )
( pernr = 323 skladnik = '0200' kwota = 22 )
( pernr = 323 skladnik = '0200' kwota = 196 )
( pernr = 323 skladnik = '0200' kwota = 220 )
( pernr = 323 skladnik = '0100' kwota = 736 )
( pernr = 323 skladnik = '0100' kwota = 5 )
( pernr = 323 skladnik = '0100' kwota = 36 )
).
LOOP AT lt_subsum USING KEY primary_key ASSIGNING FIELD-SYMBOL(<fs_subsum>)
GROUP BY ( pernr = <fs_subsum>-pernr skladnik = <fs_subsum>-skladnik group_size = GROUP SIZE group_index = GROUP INDEX ) REFERENCE INTO DATA(lo_group).
LOOP AT GROUP lo_group ASSIGNING FIELD-SYMBOL(<fs_group>).
COLLECT ls_subsum_aux INTO lt_subsum_aux.
ENDLOOP.
ENDLOOP.
IF NOT lt_subsum_aux IS INITIAL.
cl_demo_output=>display_data( lt_subsum_aux ).
ENDIF.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Ostatnio zmieniony wt paź 24, 2023 10:55 am przez K602, łącznie zmieniany 1 raz.
SAP ABAP Certified Developer
-
- Posty: 8597
- Rejestracja: wt kwie 03, 2007 4:05 pm
- Has thanked: 2042 times
- Been thanked: 1537 times
Re: Sumowanie rekordów itab o tych samych polach.
Można też tak:
Kod: Zaznacz cały
TYPES:
BEGIN OF ty_subsum,
pernr TYPE pernr_d,
skladnik TYPE lgart,
kwota TYPE maxbt_sum,
END OF ty_subsum,
tt_subsum TYPE SORTED TABLE OF ty_subsum WITH NON-UNIQUE KEY pernr skladnik.
DATA:
lt_subsum TYPE tt_subsum,
lt_subsum_aux TYPE tt_subsum,
ls_subsum_aux TYPE ty_subsum.
lt_subsum = VALUE #( ( pernr = 123 skladnik = '0200' kwota = 125 )
( pernr = 123 skladnik = '0200' kwota = 25 )
( pernr = 123 skladnik = '0200' kwota = 195 )
( pernr = 123 skladnik = '0200' kwota = 225 )
( pernr = 323 skladnik = '0200' kwota = 121 )
( pernr = 323 skladnik = '0200' kwota = 22 )
( pernr = 323 skladnik = '0200' kwota = 196 )
( pernr = 323 skladnik = '0200' kwota = 220 )
( pernr = 323 skladnik = '0100' kwota = 736 )
( pernr = 323 skladnik = '0100' kwota = 5 )
( pernr = 323 skladnik = '0100' kwota = 36 )
).
LOOP AT lt_subsum INTO DATA(subsum).
AT END OF skladnik.
SUM.
WRITE: / subsum-pernr, subsum-skladnik, subsum-kwota.
ENDAT.
ENDLOOP.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
-
- Posty: 8597
- Rejestracja: wt kwie 03, 2007 4:05 pm
- Has thanked: 2042 times
- Been thanked: 1537 times
Re: Sumowanie rekordów itab o tych samych polach.
Można jeszcze tak, korzystając z nowej składni ABAP:
Tak jest niby bardziej nowocześnie, ale moim zdaniem mniej przejrzyście i jest to trudniejsze do debuggowania - całe poniższe wyrażenie jest wykonywane w jednym kroku:
Ciekaw jestem Waszych opinii o tych nowych wyrażeniach jak FOR, REDUCE etc.
Kod: Zaznacz cały
TYPES:
BEGIN OF ty_subsum,
pernr TYPE pernr_d,
skladnik TYPE lgart,
kwota TYPE maxbt_sum,
END OF ty_subsum,
tt_subsum TYPE SORTED TABLE OF ty_subsum WITH NON-UNIQUE KEY pernr skladnik,
BEGIN OF ty_subkey,
pernr TYPE pernr_d,
skladnik TYPE lgart,
END OF ty_subkey,
tt_subkey TYPE SORTED TABLE OF ty_subkey WITH NON-UNIQUE KEY pernr skladnik.
DATA:
lt_subsum TYPE tt_subsum,
lt_subsum_aux TYPE tt_subsum,
ls_subsum_aux TYPE ty_subsum.
lt_subsum = VALUE #( ( pernr = 123 skladnik = '0200' kwota = 125 )
( pernr = 123 skladnik = '0200' kwota = 25 )
( pernr = 123 skladnik = '0200' kwota = 195 )
( pernr = 123 skladnik = '0200' kwota = 225 )
( pernr = 323 skladnik = '0200' kwota = 121 )
( pernr = 323 skladnik = '0200' kwota = 22 )
( pernr = 323 skladnik = '0200' kwota = 196 )
( pernr = 323 skladnik = '0200' kwota = 220 )
( pernr = 323 skladnik = '0100' kwota = 736 )
( pernr = 323 skladnik = '0100' kwota = 5 )
( pernr = 323 skladnik = '0100' kwota = 36 )
).
lt_subsum_aux = VALUE #(
FOR GROUPS <groupkey> OF subsum IN lt_subsum GROUP BY ( pernr = subsum-pernr skladnik = subsum-skladnik )
( pernr = <groupkey>-pernr
skladnik = <groupkey>-skladnik
kwota = REDUCE #(
INIT k = 0
FOR subgroup IN GROUP <groupkey> NEXT k = k + subgroup-kwota ) ) ).
IF NOT lt_subsum_aux IS INITIAL.
cl_demo_output=>display_data( lt_subsum_aux ).
ENDIF.
Tak jest niby bardziej nowocześnie, ale moim zdaniem mniej przejrzyście i jest to trudniejsze do debuggowania - całe poniższe wyrażenie jest wykonywane w jednym kroku:
Kod: Zaznacz cały
lt_subsum_aux = VALUE #(
FOR GROUPS <groupkey> OF subsum IN lt_subsum GROUP BY ( pernr = subsum-pernr skladnik = subsum-skladnik )
( pernr = <groupkey>-pernr
skladnik = <groupkey>-skladnik
kwota = REDUCE #(
INIT k = 0
FOR subgroup IN GROUP <groupkey> NEXT k = k + subgroup-kwota ) ) ).
Ciekaw jestem Waszych opinii o tych nowych wyrażeniach jak FOR, REDUCE etc.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Re: Sumowanie rekordów itab o tych samych polach.
FOR jak najbardziej używam do wyrażenia VALUE, iterując jakąś wzorcową tabelę. Dobre, proste i wygodne. REDUCE jakoś do mnie nie przemawia...
Ostatnio zmieniony pt paź 27, 2023 10:53 am przez K602, łącznie zmieniany 1 raz.
SAP ABAP Certified Developer
Re: Sumowanie rekordów itab o tych samych polach.
Jak mam być szczery, to rozwiązanie Dominika najbardziej do mnie przemawia, ładne, proste, wygodnie mogłem sobie je rozwinąć.
Tak czy inaczej wszystkim dziękuję za podpowiedzi
Tak czy inaczej wszystkim dziękuję za podpowiedzi

-
- Posty: 568
- Rejestracja: śr kwie 04, 2007 4:32 pm
- Lokalizacja: Poznań
- Has thanked: 10 times
- Been thanked: 174 times
Re: Sumowanie rekordów itab o tych samych polach.
Można też wykorzystać stare dobre COLLECT:
Kod: Zaznacz cały
types:
begin of ty_subsum,
pernr type pernr_d,
skladnik type lgart,
kwota type maxbt_sum,
end of ty_subsum,
begin of summarized_dataset_type,
id type char12, " length of pernr_d (8 chars) + lgart (4 chars)
amount type maxbt_sum,
end of summarized_dataset_type,
tt_subsum type sorted table of ty_subsum with non-unique key pernr skladnik.
data:
subsums type tt_subsum,
sums type hashed table of summarized_dataset_type with unique key id.
subsums = value #( ( pernr = 123 skladnik = '0200' kwota = 125 )
( pernr = 123 skladnik = '0200' kwota = 25 )
( pernr = 123 skladnik = '0200' kwota = 195 )
( pernr = 123 skladnik = '0200' kwota = 225 )
( pernr = 323 skladnik = '0200' kwota = 121 )
( pernr = 323 skladnik = '0200' kwota = 22 )
( pernr = 323 skladnik = '0200' kwota = 196 )
( pernr = 323 skladnik = '0200' kwota = 220 )
( pernr = 323 skladnik = '0100' kwota = 736 )
( pernr = 323 skladnik = '0100' kwota = 5 )
( pernr = 323 skladnik = '0100' kwota = 36 ) ).
" create summarized dataset
loop at subsums into data(subsum).
collect value summarized_dataset_type( id = subsum-pernr && subsum-skladnik
amount = subsum-kwota )
into sums.
endloop.
" screen output
loop at sums into data(sum).
write: / sum-id(8), sum-id+8(*), sum-amount.
endloop.
Pozdrawiam,
Jacek Witczak
http://novertio.pl
Jacek Witczak
http://novertio.pl
Re: Sumowanie rekordów itab o tych samych polach.
Tak, oczywiście, że tak.
To wszystko. I teraz przy 3 000 000 rekordów, czas wykonania: ~0,83s
Podsumowując, moja strata czasowa na LOOP AT GROUP, polegała na robieniu 2x tego samego. Najpierw LOOP AT..., a potem w COLLECT, a przecież COLLECT robi to sam
Kod: Zaznacz cały
LOOP AT lt_subsum USING KEY primary_key ASSIGNING FIELD-SYMBOL(<fs_subsum>).
COLLECT <fs_subsum> INTO lt_subsum_aux.
ENDLOOP.
Podsumowując, moja strata czasowa na LOOP AT GROUP, polegała na robieniu 2x tego samego. Najpierw LOOP AT..., a potem w COLLECT, a przecież COLLECT robi to sam

SAP ABAP Certified Developer
-
- Posty: 8597
- Rejestracja: wt kwie 03, 2007 4:05 pm
- Has thanked: 2042 times
- Been thanked: 1537 times
Re: Sumowanie rekordów itab o tych samych polach.
Wykorzystanie COLLECT czy SUM wymaga określonej struktury tabeli
Dlatego czasami łatwiej jest sumowanie zrobić ręcznie np. w taki sposób:
SAP Help - COLLECT pisze:All components that are not part of the primary table key must have a numeric data type.
Dlatego czasami łatwiej jest sumowanie zrobić ręcznie np. w taki sposób:
Kod: Zaznacz cały
TYPES:
BEGIN OF ty_subsum,
pernr TYPE pernr_d,
skladnik TYPE lgart,
kwota TYPE maxbt_sum,
END OF ty_subsum,
tt_subsum TYPE SORTED TABLE OF ty_subsum WITH NON-UNIQUE KEY pernr skladnik,
BEGIN OF ty_subkey,
pernr TYPE pernr_d,
skladnik TYPE lgart,
END OF ty_subkey,
tt_subkey TYPE SORTED TABLE OF ty_subkey WITH NON-UNIQUE KEY pernr skladnik.
DATA:
lt_subsum TYPE tt_subsum,
lt_subsum_aux TYPE tt_subsum,
ls_subsum_aux TYPE ty_subsum.
lt_subsum = VALUE #( ( pernr = 123 skladnik = '0200' kwota = 125 )
( pernr = 123 skladnik = '0200' kwota = 25 )
( pernr = 123 skladnik = '0200' kwota = 195 )
( pernr = 123 skladnik = '0200' kwota = 225 )
( pernr = 323 skladnik = '0200' kwota = 121 )
( pernr = 323 skladnik = '0200' kwota = 22 )
( pernr = 323 skladnik = '0200' kwota = 196 )
( pernr = 323 skladnik = '0200' kwota = 220 )
( pernr = 323 skladnik = '0100' kwota = 736 )
( pernr = 323 skladnik = '0100' kwota = 5 )
( pernr = 323 skladnik = '0100' kwota = 36 )
).
DATA:
pernr TYPE pernr_d,
skladnik TYPE lgart,
kwota TYPE maxbt_sum.
LOOP AT lt_subsum ASSIGNING FIELD-SYMBOL(<subsum>).
IF pernr <> <subsum>-pernr OR
skladnik <> <subsum>-skladnik.
IF pernr IS NOT INITIAL.
WRITE: / pernr, skladnik, kwota.
ENDIF.
CLEAR: kwota.
pernr = <subsum>-pernr.
skladnik = <subsum>-skladnik.
ENDIF.
kwota = kwota + <subsum>-kwota.
ENDLOOP.
WRITE: / pernr, skladnik, kwota.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
-
- Posty: 568
- Rejestracja: śr kwie 04, 2007 4:32 pm
- Lokalizacja: Poznań
- Has thanked: 10 times
- Been thanked: 174 times
Re: Sumowanie rekordów itab o tych samych polach.
Miałem bardzo mocne podejrzenie, że COLLECT będzie szybkie 
Tak jak napisał Dominik - COLLECT ma kilka podstawowych założeń aby zadziałać - plus kilka założeń dotyczących tego aby ta instrukcja działała efektywnie. Jednym z takich podstawowych założeń/wymogów jest ten, który mówi żeby nie stosować COLLECT do tabel standardowych albo sortowanych bez unikalnego klucza. W takim przypadku system musi stosować dodatkową (na boku) obsługę bufora z hashem co może prowadzić do pogorszenia wydajności - złożoność liniowa O(n) zamiast pożądanej logarytmicznej O(log n). Dotyczy to zwłaszcza sytuacji gdy ten poboczny bufor z hashem jest często modyfikowany na skutek operacji zmian w tabeli wewnętrznej (można wykorzystać moduł funkcyjny ABL_TABLE_HASH_STATE do sprawdzenia czy dana tabela standardowa jest ok dla instrukcji COLLECT).
I tak poza konkursem - widzę, że notacja węgierska wiecznie żywa

Tak jak napisał Dominik - COLLECT ma kilka podstawowych założeń aby zadziałać - plus kilka założeń dotyczących tego aby ta instrukcja działała efektywnie. Jednym z takich podstawowych założeń/wymogów jest ten, który mówi żeby nie stosować COLLECT do tabel standardowych albo sortowanych bez unikalnego klucza. W takim przypadku system musi stosować dodatkową (na boku) obsługę bufora z hashem co może prowadzić do pogorszenia wydajności - złożoność liniowa O(n) zamiast pożądanej logarytmicznej O(log n). Dotyczy to zwłaszcza sytuacji gdy ten poboczny bufor z hashem jest często modyfikowany na skutek operacji zmian w tabeli wewnętrznej (można wykorzystać moduł funkcyjny ABL_TABLE_HASH_STATE do sprawdzenia czy dana tabela standardowa jest ok dla instrukcji COLLECT).
I tak poza konkursem - widzę, że notacja węgierska wiecznie żywa

Pozdrawiam,
Jacek Witczak
http://novertio.pl
Jacek Witczak
http://novertio.pl
-
- Posty: 8597
- Rejestracja: wt kwie 03, 2007 4:05 pm
- Has thanked: 2042 times
- Been thanked: 1537 times
Re: Sumowanie rekordów itab o tych samych polach.
Ale w tym przypadku nie zrobimy klucza unikalnego, bo wartości co do klucza, mogą/powtarzają się...
A pewnie

SAP ABAP Certified Developer
-
- Posty: 568
- Rejestracja: śr kwie 04, 2007 4:32 pm
- Lokalizacja: Poznań
- Has thanked: 10 times
- Been thanked: 174 times
Re: Sumowanie rekordów itab o tych samych polach.
Nie patrzymy na tabelę źródłową (tę z podsumami) ale wynikową (czyli tę z sumami, budowaną przez COLLECT) - zobacz, że zastosowałem tabele hashowaną, która z definicji ma zawsze unikalny klucz.
Pozdrawiam,
Jacek Witczak
http://novertio.pl
Jacek Witczak
http://novertio.pl