Sumowanie rekordów itab o tych samych polach.

Jeśli programujesz, administrujesz, integrujesz i masz wątpliwość lub obawę, to właśnie najlepsze miejsce dla Ciebie. Pisz śmiało...
emil
Posty: 133
Rejestracja: pt gru 27, 2019 11:02 am
Has thanked: 29 times
Been thanked: 42 times

Sumowanie rekordów itab o tych samych polach.

Post autor: emil »

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?
K602
Posty: 132
Rejestracja: śr sie 24, 2022 11:50 am
Has thanked: 60 times
Been thanked: 86 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: K602 »

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:

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
dominik.tylczynski
Posty: 8289
Rejestracja: wt kwie 03, 2007 4:05 pm
Has thanked: 1903 times
Been thanked: 1470 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: dominik.tylczynski »

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.
20231023_135056.png
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
dominik.tylczynski
Posty: 8289
Rejestracja: wt kwie 03, 2007 4:05 pm
Has thanked: 1903 times
Been thanked: 1470 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: dominik.tylczynski »

Można jeszcze tak, korzystając z nowej składni ABAP:

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.
20231024_102326.png

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.
K602
Posty: 132
Rejestracja: śr sie 24, 2022 11:50 am
Has thanked: 60 times
Been thanked: 86 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: K602 »

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
emil
Posty: 133
Rejestracja: pt gru 27, 2019 11:02 am
Has thanked: 29 times
Been thanked: 42 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: emil »

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 :)
yacol
Posty: 561
Rejestracja: śr kwie 04, 2007 4:32 pm
Lokalizacja: Poznań
Has thanked: 9 times
Been thanked: 165 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: yacol »

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
K602
Posty: 132
Rejestracja: śr sie 24, 2022 11:50 am
Has thanked: 60 times
Been thanked: 86 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: K602 »

Tak, oczywiście, że tak.

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.
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 :)
SAP ABAP Certified Developer
dominik.tylczynski
Posty: 8289
Rejestracja: wt kwie 03, 2007 4:05 pm
Has thanked: 1903 times
Been thanked: 1470 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: dominik.tylczynski »

Wykorzystanie COLLECT czy SUM wymaga określonej struktury tabeli
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.
20231027_111812.png
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
yacol
Posty: 561
Rejestracja: śr kwie 04, 2007 4:32 pm
Lokalizacja: Poznań
Has thanked: 9 times
Been thanked: 165 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: yacol »

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 :D
Pozdrawiam,

Jacek Witczak
http://novertio.pl
dominik.tylczynski
Posty: 8289
Rejestracja: wt kwie 03, 2007 4:05 pm
Has thanked: 1903 times
Been thanked: 1470 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: dominik.tylczynski »

yacol pisze: pt paź 27, 2023 12:55 pm I tak poza konkursem - widzę, że notacja węgierska wiecznie żywa :D
Old habits die hard :D
K602
Posty: 132
Rejestracja: śr sie 24, 2022 11:50 am
Has thanked: 60 times
Been thanked: 86 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: K602 »

yacol pisze: pt paź 27, 2023 12:55 pm (...) 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. (...)
Ale w tym przypadku nie zrobimy klucza unikalnego, bo wartości co do klucza, mogą/powtarzają się...

yacol pisze: pt paź 27, 2023 12:55 pm I tak poza konkursem - widzę, że notacja węgierska wiecznie żywa :D
A pewnie ;)
SAP ABAP Certified Developer
yacol
Posty: 561
Rejestracja: śr kwie 04, 2007 4:32 pm
Lokalizacja: Poznań
Has thanked: 9 times
Been thanked: 165 times

Re: Sumowanie rekordów itab o tych samych polach.

Post autor: yacol »

K602 pisze: pt paź 27, 2023 1:42 pm Ale w tym przypadku nie zrobimy klucza unikalnego, bo wartości co do klucza, mogą/powtarzają się...
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