LET - FOR - BASE Performance

Getting started ... Alles für einen gelungenen Start.
15 Beiträge • Seite 1 von 1
15 Beiträge Seite 1 von 1

LET - FOR - BASE Performance

Beitrag von Wann (ForumUser / 61 / 3 / 15 ) »
Ich habe ein Programm, was als Ergebnis genau das liefert, was ich haben möchte. Es ist ein wunderschönes Beispiel für die Verwendung von LET(FOR und BASE). Und es ist grauenvoll langsam.

Code: Alles auswählen.

REPORT zfi_select_beleg.
TABLES: bkpf, bseg.


SELECT-OPTIONS: s_bukrs FOR bkpf-bukrs,
                s_gjahr FOR bkpf-gjahr,
                s_hkont FOR bseg-hkont.

TYPES: BEGIN OF header,
         bukrs TYPE bkpf-bukrs,
         belnr TYPE bkpf-belnr,
         gjahr TYPE bkpf-gjahr,
         usnam TYPE usnam,
       END OF header,
       t_header TYPE STANDARD TABLE OF header WITH KEY bukrs belnr gjahr,
       BEGIN OF position,
         bukrs TYPE bkpf-bukrs,
         belnr TYPE bkpf-belnr,
         gjahr TYPE bkpf-gjahr,
         buzei TYPE bseg-buzei,
         dmbtr TYPE dmbtr,
         hkont TYPE bseg-hkont,
         shkzg TYPE bseg-shkzg,
       END OF position,
       t_position TYPE STANDARD TABLE OF position WITH KEY bukrs belnr buzei gjahr,
       BEGIN OF ausgabe,
         bukrs TYPE bkpf-bukrs,
         belnr TYPE bkpf-belnr,
         buzei TYPE bseg-buzei,
         gjahr TYPE bkpf-gjahr,
         dmbtr TYPE dmbtr,
         hkont TYPE bseg-hkont,
         shkzg TYPE bseg-shkzg,
         usnam TYPE usnam,
       END OF ausgabe,
       t_ausgabe TYPE STANDARD TABLE OF ausgabe WITH KEY bukrs belnr buzei gjahr.

DATA : lt_header  TYPE  t_header,
       lt_bseg    TYPE  t_position,
       lt_ausgabe TYPE  t_ausgabe.

* ----------------------------------------------------------

* Schlüsselfelder BKPF + Userkennung
SELECT bukrs, belnr, gjahr, usnam FROM bkpf
  INTO CORRESPONDING FIELDS OF TABLE @lt_header
  WHERE bukrs IN @s_bukrs
    AND gjahr IN @s_gjahr    .

* Schlüsselfelder BSEG + Betrag, Sachkonto, Soll/Haben-Kennzeichen
SELECT bukrs, belnr, buzei, gjahr, dmbtr, hkont, shkzg  FROM bseg
  INTO CORRESPONDING FIELDS OF TABLE @lt_bseg
  WHERE bukrs IN @s_bukrs
    AND gjahr IN @s_gjahr
    AND hkont IN @s_hkont.

* Alles durch Magie in die interne Tabelle lt_alv für die Ausgabe. Nur leider zu langsam.

DATA(lt_alv) = VALUE t_ausgabe(
     FOR ls_bseg IN lt_bseg
     FOR ls_bkpf IN lt_header
FROM line_index( lt_ausgabe[
  bukrs = ls_bkpf-bukrs
  gjahr = ls_bkpf-gjahr
  belnr = ls_bkpf-belnr    ] )
WHERE
 ( bukrs = ls_bseg-bukrs AND
   gjahr = ls_bseg-gjahr AND
   belnr = ls_bseg-belnr    )
LET ls_ausgabe = VALUE ausgabe( usnam = ls_bkpf-usnam )
   IN ( CORRESPONDING #( BASE ( ls_ausgabe ) ls_bseg ) ) ).


DATA go_functions TYPE REF TO cl_salv_functions_list.

cl_salv_table=>factory(
                  IMPORTING
                    r_salv_table = DATA(lo_alv)
                  CHANGING
                    t_table = lt_alv ).

go_functions = lo_alv->get_functions( ).
go_functions->set_all( abap_true ).

lo_alv->display( ).
Also ich habe bei meinem Test zum Zeitpunkt, wo ich alles in die interne Tabelle lt_alv schiebe etwa 30.000 Einträge in lt_header und ~70.000 Einträge in lt_bseg. Das Erzeugen der internen Tabelle lt_alv dauert dann leider zu lange. Irgendwie sehe ich den Fehler nicht.

gesponsert
Stellenangebote auf ABAPforum.com schalten
kostenfrei für Ausbildungsberufe und Werksstudenten


Re: LET - FOR - BASE Performance

Beitrag von a-dead-trousers (Top Expert / 4438 / 226 / 1193 ) »
So wie ich das interpretieren würde, hast du in deinem ersten VALUE ein Kreuzprodukt von 30.000x70.000 bzw. 2.100.000.000 Durchläufen.
Vielleicht hilft hier eventuell ein SORTED TABLE weiter, damit die WHERE Bedingung eine bessere Grundlage hat. Nichtsdestotroz verstehe ich das Konstrukt sowieso nur zur Hälfte:
Auf was beziehen sich die Angaben von BUKRS, GJAHR und BELNR im WHERE? Auf LT_BSEG, LT_HEADER oder LT_AUSGABE?
Was macht das FROM mit der LINE_NUMBER aus LT_AUSGABE? Bezieht sich das nicht auf den Schleifenbeginn in der FOR Bedingung? Ist dann überhaupt gesichert, dass die Zeilen in LT_BSEG und LT_HEADER mit LT_AUSGABE übereinstimmen?

Bei einem dermaßen verschachtelten Aufbau würde ich daher dann doch die "klassische" Variante mit LOOP AT bzw. READ TABLE (mit BINARY SEARCH) bevorzugen.

Folgende Benutzer bedankten sich beim Autor a-dead-trousers für den Beitrag:
Wann

Theory is when you know something, but it doesn't work.
Practice is when something works, but you don't know why.
Programmers combine theory and practice: Nothing works and they don't know why.

ECC: 6.18
Basis: 7.50

Re: LET - FOR - BASE Performance

Beitrag von DeathAndPain (Top Expert / 1967 / 261 / 415 ) »
Von READ TABLE halte ich in der heutigen Zeit nicht mehr viel, außer man kann die Tabelle nicht als sortiert oder gehasht definieren oder benötigt zusätzlich SY-TABIX. Davon abgesehen würde ich adt aber recht geben.

Was mir außerdem Gedanken macht, ist, dass die BASE-Klausel den Namen der zuzuweisenden Tabelle selbst enthält. Ich weiß nicht, wie intelligent der ABAP-Interpreter damit umgeht, aber formal wird die ganze Tabelle mit all ihren Zeilen bei jedem einzelnen Durchlauf komplett kopiert, dann verworfen und über die Zuweisung neu aufgebaut. Es ist ja eine Zuweisung, d.h. Du fügst der Tabelle nicht eine Zeile hinzu, sondern weist ihr einen komplett neuen Inhalt zu. Auch wenn bei der Ermittlung dieses Inhalts der alte Inhalt der Tabelle über das BASE mit eingeht.

Es kann natürlich sein, dass die SAP eine Logik eingebaut hat, die das erkennt und da intelligenter mit umgeht, aber wenn nicht, dann wird ein herkömmlicher APPEND bzw. (bei sortierter Tabelle) INSERT INTO TABLE drastisch performanter sein.

Folgende Benutzer bedankten sich beim Autor DeathAndPain für den Beitrag:
Wann


Re: LET - FOR - BASE Performance

Beitrag von rob_abc (Specialist / 122 / 33 / 49 ) »
Ich hatte mal einen kleinen Testreport geschrieben, weil das mit value base schon mal hier im Forum behauptet wurde. Mittels SAT ausgeführt ist es zumindest in diesem Beispiel kein Unterschied, ob man INSERT, APPEND oder BASE verwendet. Deutlich schneller ist allerdings SELECT gewesen.

Code: Alles auswählen.

REPORT.

CLASS lcl_report DEFINITION.

  PUBLIC SECTION.
    TYPES:
      BEGIN OF ty_text,
        text TYPE c LENGTH 100,
      END OF ty_text,
      tty_texts TYPE STANDARD TABLE OF ty_text WITH NON-UNIQUE KEY table_line.

    CLASS-METHODS:
      main.

  PRIVATE SECTION.
    CLASS-METHODS:
      prepare_source_table,
      fill_table_with_base,
      fill_table_with_append,
      fill_table_with_insert,
      fill_table_with_for,
      fill_table_with_select.

    CLASS-DATA:
      source_for    TYPE tty_texts,
      source_base   TYPE tty_texts,
      source_append TYPE tty_texts,
      source_insert TYPE tty_texts,
      source_select TYPE tty_texts.


ENDCLASS.

CLASS lcl_report IMPLEMENTATION.

  METHOD main.

    prepare_source_table( ).

    fill_table_with_select( ).
    fill_table_with_for( ).
    fill_table_with_insert( ).
    fill_table_with_append( ).
    fill_table_with_base( ).

  ENDMETHOD.

  METHOD prepare_source_table.
    DO 9999 TIMES.
      INSERT VALUE #( text =  |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam a sodales lorem. { sy-index }: for| ) INTO TABLE source_for.
      INSERT VALUE #( text =  |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam a sodales lorem. { sy-index }: base| ) INTO TABLE source_base.
      INSERT VALUE #( text =  |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam a sodales lorem. { sy-index }: append| ) INTO TABLE source_append.
      INSERT VALUE #( text =  |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam a sodales lorem. { sy-index }: insert| ) INTO TABLE source_insert.
      INSERT VALUE #( text =  |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam a sodales lorem. { sy-index }: select| ) INTO TABLE source_select.
    ENDDO.
  ENDMETHOD.

  METHOD fill_table_with_base.
    DATA rng_texts TYPE RANGE OF ty_text.
    LOOP AT source_base ASSIGNING FIELD-SYMBOL(<row>).
      rng_texts = VALUE #( BASE rng_texts ( sign = 'I' option = 'EQ' low = <row>-text ) ).
    ENDLOOP.
  ENDMETHOD.


  METHOD fill_table_with_append.
    DATA rng_texts TYPE RANGE OF ty_text.
    LOOP AT source_append ASSIGNING FIELD-SYMBOL(<row>).
      APPEND VALUE #( sign = 'I' option = 'EQ' low = CONV #( <row>-text ) ) TO rng_texts.
    ENDLOOP.
  ENDMETHOD.

  METHOD fill_table_with_insert.
    DATA rng_texts TYPE RANGE OF ty_text.
    LOOP AT source_insert ASSIGNING FIELD-SYMBOL(<row>).
      INSERT VALUE #( sign = 'I' option = 'EQ' low = CONV #( <row>-text ) ) INTO TABLE rng_texts.
    ENDLOOP.
  ENDMETHOD.

  METHOD fill_table_with_for.
    DATA rng_texts TYPE RANGE OF ty_text.
    rng_texts = VALUE #( FOR <row> IN source_for ( sign = 'I' option = 'EQ' low = <row>-text ) ).
  ENDMETHOD.

  METHOD fill_table_with_select.
    DATA rng_texts TYPE RANGE OF ty_text.
    SELECT FROM @source_select AS source
      FIELDS 'I' AS sign, 'EQ' AS option, source~text AS low
      INTO TABLE @rng_texts.
  ENDMETHOD.

ENDCLASS.

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

Folgende Benutzer bedankten sich beim Autor rob_abc für den Beitrag (Insgesamt 3):
ewxDeathAndPainWann


Re: LET - FOR - BASE Performance

Beitrag von sap_enthusiast (ForumUser / 95 / 25 / 23 ) »
Da READ TABLE heutzutage schon als "obsolet" betrachtet wird, wäre eine mögliche alternative die eingebaute Funktion

Code: Alles auswählen.

line_index( )
?
Verträgt sogar verkettete Tabellenausdrücken.
Link: https://help.sap.com/doc/abapdocu_751_i ... nction.htm

Dazu zusätzlich, wenn möglich Hashed Tabellen zu benutzen. Bei großen Datenmengen ist es schon eh geraten.

Folgende Benutzer bedankten sich beim Autor sap_enthusiast für den Beitrag:
ewx


Re: LET - FOR - BASE Performance

Beitrag von DeathAndPain (Top Expert / 1967 / 261 / 415 ) »
sap_enthusiast hat geschrieben:
19.01.2023 09:34
Da READ TABLE heutzutage schon als "obsolet" betrachtet wird, wäre eine mögliche alternative die eingebaute Funktion line_index( ) ?
Du meinst für den Fall, dass man SY-TABIX benötigt? Wenn man nur SY-TABIX benötigt und für den Fall, dass die gesuchte Zeile nicht gefunden wird, nicht den passenden Einfügeindex braucht, ist das sicherlich eine Option. Ich dachte eher an den Fall, bei dem ich den Wert einer Zeile brauche und deren Tabellenindex. Dann ist READ TABLE sicherlich besser, weil ich beides auf einmal benötige. Kommt in der Praxis nur sehr selten vor.

LINE_INDEX kann nur READ_TABLE mit Zusatz TRANSPORTING NO FIELDS sinnvoll ersetzen. Sonst müsste man dieselben Zeile zweimal suchen, einmal für ihren Wert und bei LINE_INDEX dann nochmal für ihre Zeilennummer. Das wäre weder schlank noch performant.

Mir ist auch nicht bekannt, dass READ TABLE offiziell für veraltet erklärt worden wäre. Aber von der Lesbarkeit her ist READ TABLE halt um Längen schlechter als ein ASSIGN bzw. die direkte Zuweisung an ein Feld mittels 7.40-Syntax, so dass man in meinen Augen gute Gründe braucht, trotzdem READ TABLE zu schreiben. Meist ist es dann sinnvoll, wenn man den Zusatz BINARY SEARCH braucht, weil man eine Standardtabelle in der Hand hat (wobei es dann auch wieder gute Gründe geben sollte, weshalb es eine Standardtabelle ist).

@rob_abc: Vielen Dank, so gepflegten Code sieht man selten. Demnach hat die SAP tatsächlich Optimierungen eingebaut, um diesen Fall zu erkennen und intern umzuwandeln. Täten sie das nicht und würden bei jedem Zeilendurchlauf die ganze Tabelle umkopieren, wäre eine gleiche Performance nicht möglich.

Dumm nur, dass die SELECT-Variante unter 7.50 noch nicht unterstützt wird und es für SAP HCM kein höheres Release gibt.

Re: LET - FOR - BASE Performance

Beitrag von Wann (ForumUser / 61 / 3 / 15 ) »
Schönen Dank für die Beiträge. Sorted Table hat extrem viel gebracht.

Re: LET - FOR - BASE Performance

Beitrag von black_adept (Top Expert / 4130 / 130 / 955 ) »
DeathAndPain hat geschrieben:
18.01.2023 15:33
Von READ TABLE halte ich in der heutigen Zeit nicht mehr viel, außer man kann die Tabelle nicht als sortiert oder gehasht definieren oder benötigt zusätzlich SY-TABIX.
Moin D&P,
ich verwende häufiger folgendes Konstrukt. Wie wäre das denn (elegant) ohne Read Table erreichbar?

Code: Alles auswählen.

READ TABLE t_buffer WITH TABLE KEY key_components = local_key ASSIGNING FIELD-SYMBOL(<row_for_key>).
IF sy-subrc <> 0.
  INSERT VALUE #( key_components = local_key ) INTO TABLE t_buffer ASSIGNING <row_for_key>.
 ~diverse (laufzeitintensive?) Berechnungen um Nichtschlüsselfelder zu füllen~
ENDIF.
~in <row_for_key> stehen jetzt die Daten bereit~
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de

Re: LET - FOR - BASE Performance

Beitrag von rob_abc (Specialist / 122 / 33 / 49 ) »

Code: Alles auswählen.

ASSIGN t_buffer[ key_components = local_key ] to FIELD-SYMBOL(<row_for_key>).
Ist das zu einfach gedacht?

Re: LET - FOR - BASE Performance

Beitrag von DeathAndPain (Top Expert / 1967 / 261 / 415 ) »
Nein, genau das ist die Antwort, und Deine Syntax halte ich für deutlich schöner (und nebenbei kürzer zu tippen) als die READ TABLE-Variante.

Allerdings ist black_adepts Beispiel durchaus eins, bei dem der READ TABLE Sinn ergeben kann, nämlich dann, wenn t_buffer SORTED ist. Dann bringt einem nämlich der SY-TABIX etwas, den der READ TABLE mitliefert, weil man ihn nutzen kann um zu verhindern, dass die Tabelle beim INSERT nochmals die richtige Stelle suchen muss:

Code: Alles auswählen.

READ TABLE t_buffer WITH TABLE KEY key_components = local_key ASSIGNING FIELD-SYMBOL(<row_for_key>).
IF sy-subrc <> 0.
  INSERT VALUE #( key_components = local_key ) INTO t_buffer INDEX sy-tabix ASSIGNING <row_for_key>.
 ~diverse (laufzeitintensive?) Berechnungen um Nichtschlüsselfelder zu füllen~
ENDIF.
~in <row_for_key> stehen jetzt die Daten bereit~

Re: LET - FOR - BASE Performance

Beitrag von black_adept (Top Expert / 4130 / 130 / 955 ) »
rob_abc hat geschrieben:
20.01.2023 14:47

Code: Alles auswählen.

ASSIGN t_buffer[ key_components = local_key ] to FIELD-SYMBOL(<row_for_key>).
Ist das zu einfach gedacht?
Das dumpt, wenn die Zeile nicht vorhanden ist
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de

Re: LET - FOR - BASE Performance

Beitrag von DeathAndPain (Top Expert / 1967 / 261 / 415 ) »
Da irrst Du Dich. ASSIGN dumpt niemals, sondern setzt den SY-SUBRC.

Letztlich ist das auch die viel nützlichere Verhaltensweise. An der Stelle hat die SAP mal was sinnvoll statt streng formal gemacht.

Folgende Benutzer bedankten sich beim Autor DeathAndPain für den Beitrag:
qyurryus


Re: LET - FOR - BASE Performance

Beitrag von qyurryus (Specialist / 113 / 87 / 46 ) »
DeathAndPain hat geschrieben:
20.01.2023 23:33
Da irrst Du Dich. ASSIGN dumpt niemals, sondern setzt den SY-SUBRC.

Letztlich ist das auch die viel nützlichere Verhaltensweise. An der Stelle hat die SAP mal was sinnvoll statt streng formal gemacht.
Huh... dachte die Tabellenzuweisung würde immer dumpen, aber tatsächlich, im Kontext von "ASSIGN" dumpt es nicht, gut zu wissen!

Code: Alles auswählen.

  DATA(tab) = VALUE str_tab( ( `AA` ) ).

  " kein Dump
  ASSIGN tab[ 2 ] TO FIELD-SYMBOL(<xd>).

  " Dump
  DATA(test) = tab[ 2 ].

Re: LET - FOR - BASE Performance

Beitrag von black_adept (Top Expert / 4130 / 130 / 955 ) »
DeathAndPain hat geschrieben:
20.01.2023 23:33
Da irrst Du Dich. ASSIGN dumpt niemals, sondern setzt den SY-SUBRC.
Hast recht. Danke
live long and prosper
Stefan Schmöcker

email: stefan@schmoecker.de

Re: LET - FOR - BASE Performance

Beitrag von DeathAndPain (Top Expert / 1967 / 261 / 415 ) »
qyurryus hat geschrieben:
23.01.2023 08:38
Huh... dachte die Tabellenzuweisung würde immer dumpen, aber tatsächlich, im Kontext von "ASSIGN" dumpt es nicht, gut zu wissen!

Code: Alles auswählen.

  DATA(tab) = VALUE str_tab( ( `AA` ) ).

  " kein Dump
  ASSIGN tab[ 2 ] TO FIELD-SYMBOL(<xd>).

  " Dump
  DATA(test) = tab[ 2 ].
Es gibt auch noch andere Kontexte, bei denen die Tabellenzuweisung nicht dumpt, wobei das bei denen offensichtlicher ist, beispielsweise bei

IF LINE_EXISTS( tab[ 2 ] ).

oder natürlich bei VALUE mit OPTIONAL (dafür ist der OPTIONAL ja gerade da).

Ich persönlich finde es schade, dass es überhaupt dumpt, denn dadurch kann man die direkte Wertzuweisung an Felder wie in Deinem Beispiel nur sehr selten nutzen und muss dafür immer den umständlichen VALUE...OPTIONAL (oder noch schlimmer: TRY/ENDTRY) bauen. READ TABLE hat auch nicht gedumpt, und keinen hat's gestört, also warum muss die direkte Zuweisung dumpen? [/rant]

Seite 1 von 1

Vergleichbare Themen

3
Antw.
2724
Views
Performance von INE vs. EEQ
von Birdy » 14.08.2013 11:35 • Verfasst in ABAP® für Anfänger
3
Antw.
2304
Views
Performance
von schick » 29.03.2018 14:48 • Verfasst in ABAP® für Anfänger
3
Antw.
3009
Views
Performance
von SAP_ENTWICKLER » 19.02.2018 07:06 • Verfasst in SAP HANA für Anfänger
2
Antw.
2204
Views
SQL und Performance
von Hagbard » 24.11.2005 08:51 • Verfasst in ABAP® für Anfänger
1
Antw.
2131
Views
IDOC-Performance
von Thomas R. » 18.09.2007 15:02 • Verfasst in Basis

Newsletter Anmeldung

Keine Beiträge verpassen! Wöchentlich versenden wir lesenwerte Beiträge aus unserer Community.
Die letzte Ausgabe findest du hier.
Details zum Versandverfahren und zu Ihren Widerrufsmöglichkeiten findest du in unserer Datenschutzerklärung.