Das sehe ich deutlich anders und bin von meinem neuen Coding durchaus überzeugt.D&P: Jetzt ist das Coding zwar überarbeitet - aber man sieht, dass du es auf die Schnelle gemacht hast. Was rausgekommen ist, ist ein typisches Beispiel für unleserliches Coding.
Das habe ich auch gedacht, als ich damals den Code erstellt habe. Aber es stimmt eben nicht, und genau deshalb habe ich den Code jetzt so geändert, dass es stimmt! Der minimale Wert, den die Uhr anzeigen kann, ist eben nicht 0%, sondern 1% (weil er bei 0 gar keine Uhr anzeigt). Dieser FB stellt mir also nicht 100 Schritte zur Verfügung, sondern nur 99, beginnend mit 1! Also muss ich die Laufzeit meines LOOP eben nicht in 100, sondern in 99 Teile aufteilen. Das ist die Einteilung, die mir der FB nur bietet. Ich brauche also gar keine Pro"cent", sondern Pro"neunundneunzig"! Damit ist das, was ich da mache, mathematisch absolut richtig und konsequent. Und als Ergebnis dieser Konsequenz fällt die Erfordernis der IF-Krücke weg. (Der Name des "PERCENTAGE"-Parameters des FB ist insofern irreführend.)Du willst eine Prozentzahl ermitteln. Da kann man doch nicht mit 99 multiplizieren und nachher noch +1 rechnen, damit komische Symptome dann korrigiert werden. Die Prozentzahl die du haben willst ist immer! 100 * sy-tabix / GES.
Quälen? Wenn ich Dich nicht kennen würde, dann würde ich jetzt fragen, ob Du ernsthaft ein Problem hast, ein Coding zu verstehen, das gerade mal aus 7 Zeilen besteht (zudem einfachen Zeilen, also nix mit verschachtelten Methoden und FOR und bla). Mein Fokus hier lag klar auf kurzem, performanten Code. Das Coding nachvollziehen soll gar niemand; wie es zu verwenden ist, steht ja im Kommentar oben drin (und wer es dennoch nachvollziehen möchte, der muss einem siebenzeiligen Code gewachsen sein).Gewissermaßen macht das tatsächlich 5% Schritte - aber nur nachdem man sich durch das Coding gequält hat und die ganze >-Zeichenarie auswertet .
Code: Alles auswählen.
LOOP AT gt_output ASSIGNING <gs_output>.
output_idx = sy-tabix.
*Lesen Warengruppenbezeichnung
IF NOT <gs_output>-matkl IS INITIAL.
SELECT SINGLE *
INTO gs_t023t
FROM T023T
WHERE spras = 'DE'
AND matkl = <gs_output>-matkl.
IF sy-subrc = 0.
<gs_output>-wgbez = gs_t023t-wgbez.
ENDIF.
ENDIF.
*Lesen Disponentenbezeichnung
IF NOT <gs_output>-dispo IS INITIAL.
SELECT SINGLE *
INTO gs_t024d
FROM T024D
WHERE werks = '1000'
AND dispo = <gs_output>-dispo.
IF sy-subrc = 0.
<gs_output>-dsnam = gs_t024d-dsnam.
ENDIF.
ENDIF.
*Lesen Positionstypenpruppe
IF NOT <gs_output>-matnr IS INITIAL.
SELECT SINGLE *
INTO gs_mvke
FROM mvke
WHERE matnr = <gs_output>-matnr
AND vkorg = '1000'
AND vtweg = '10'.
IF sy-subrc = 0.
<gs_output>-mtpos = gs_mvke-mtpos.
ENDIF.
CONCATENATE 'Lesen der Bedarfs-/Bestandsliste bei matnr' <gs_output>-matnr INTO gv_text SEPARATED BY space.
CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
EXPORTING
* PERCENTAGE = 0
TEXT = gv_text
.
CLEAR gt_bestandliste. REFRESH gt_bestandliste.
*Lesen der Bedarfs-/Bestandsliste
CALL FUNCTION 'MD_STOCK_REQUIREMENTS_LIST_API'
EXPORTING
* PLSCN =
MATNR = <gs_output>-matnr
WERKS = '1000'
* BERID =
* ERGBZ =
* AFIBZ =
* INPER =
* DISPLAY_LIST_MDPSX =
* DISPLAY_LIST_MDEZX =
* DISPLAY_LIST_MDSUX =
* NOBUF =
* PLAUF =
IMPORTING
* E_MT61D =
E_MDKP = gs_mdkp
* E_CM61M =
* E_MDSTA =
* E_ERGBZ =
TABLES
* MDPSX =
MDEZX = gt_bestandliste
* MDSUX =
EXCEPTIONS
MATERIAL_PLANT_NOT_FOUND = 1
PLANT_NOT_FOUND = 2
OTHERS = 3
.
IF SY-SUBRC <> 0.
MESSAGE ID SY-MSGID TYPE SY-MSGTY NUMBER SY-MSGNO
WITH SY-MSGV1 SY-MSGV2 SY-MSGV3 SY-MSGV4.
ELSE.
gs_output-hoebe = gs_mdkp-hoebe.
CLEAR gs_mdkp.
SORT gt_bestandliste BY delb0 DESCENDING.
DELETE gt_bestandliste WHERE delb0 = 'AR-RES'.
gt_bestandliste_temp[] = gt_bestandliste[].
LOOP AT gt_bestandliste ASSIGNING <gs_bestandliste> WHERE delb0 = 'W-BEST' OR delb0 = 'KD-BST' OR delb0 = 'K-AUFT' OR delb0 = 'LIEFER' OR delb0 = 'SK-BED'.
best_idx = sy-tabix.
best_idx_n = best_idx.
best_idx_1000 = best_idx mod 1000.
if best_idx_1000 = 0.
CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
EXPORTING
* PERCENTAGE = 0
TEXT = best_idx_n
.
endif.
*W-BEST = Werkbestand. Hier wird die verfügbare Menge von Werkbestand gelesen
IF <gs_bestandliste>-delb0 = 'W-BEST'.
gs_output-wbmng02 = <gs_bestandliste>-mng02.
ENDIF.
*KD-BST = Kundeneinzelbestand. Hier wird die Summe der Kundeneinzelnebestände gelesen
IF <gs_bestandliste>-delb0 = 'KD-BST'.
<gs_output>-sum_kb = <gs_output>-sum_kb + <gs_bestandliste>-mng02.
*Kennzeichen"X" setzen,wenn mindestens ein negativer Kundenauftragsbestand vorliegt
IF <gs_bestandliste>-mng02 < 0.
<gs_output>-nkbflag = 'X'.
ENDIF.
"Kennzeichen "X" setzen, wenn Kundenauftragsbestände ohne Vorgang existieren, dh, wenn mng01 <> 0 und nächste Eintrag ist auch KD-BST und hat nicht gleiche planr
IF <gs_bestandliste>-mng01 <> 0.
IF <gs_output>-ovflag IS INITIAL.
i = best_idx + 1.
READ TABLE gt_bestandliste_temp INDEX i INTO gs_bestandliste_temp.
IF sy-subrc = 0.
IF gs_bestandliste_temp-planr <> <gs_bestandliste>-planr.
<gs_output>-ovflag = 'X'.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
IF <gs_bestandliste>-delb0 = 'K-AUFT'.
<gs_output>-sum_bdm_kauf = <gs_output>-sum_bdm_kauf + <gs_bestandliste>-mng01.
ENDIF.
IF <gs_bestandliste>-delb0 = 'LIEFER'.
<gs_output>-sum_bdm_liefer = <gs_output>-sum_bdm_liefer + <gs_bestandliste>-mng01.
ENDIF.
IF <gs_bestandliste>-delb0 = 'SK-BED'.
<gs_output>-sum_bdm_sek = <gs_output>-sum_bdm_sek + <gs_bestandliste>-mng01.
ENDIF.
CLEAR <gs_bestandliste>-mng01.
CLEAR <gs_bestandliste>-mng02.
ENDLOOP.
ENDIF.
CLEAR gt_bestandliste_temp. REFRESH gt_bestandliste_temp.
CLEAR gt_mdez_sum. REFRESH gt_mdez_sum.
CLEAR gt_mdez. REFRESH gt_mdez.
ENDIF.
*Wenn ckeckbox gesetzt ist, werden dann das letzte WE-Buchung
IF chkbox = 'X'.
CONCATENATE 'Lesen Tabelle mseg bei ' <gs_output>-matnr INTO gv_text SEPARATED BY space.
* CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
* EXPORTING
** PERCENTAGE = 0
* TEXT = gv_text
* .
*Abfrage, ob Wareneingangsbuchung von einem Material vorhanden ist.
SELECT MBLNR MJAHR MATNR BWART
INTO CORRESPONDING FIELDS OF TABLE gt_mseg
FROM mseg
WHERE matnr = <gs_output>-matnr
AND werks = '1000'
AND ( bwart = '101' OR bwart = '601').
IF sy-subrc = 0.
READ TABLE gt_mseg WITH KEY bwart = '101' INTO gs_mseg.
IF sy-subrc = 0.
<gs_output>-weflag = 'X'.
SORT gt_mseg BY bwart ASCENDING mjahr DESCENDING mblnr DESCENDING.
*Ermitteln das Datum der letzten Wareneingangsbuchung
READ TABLE gt_mseg INDEX 1 INTO gs_mseg.
IF sy-subrc = 0.
SELECT SINGLE *
INTO gs_mkpf
FROM mkpf
WHERE mblnr = gs_mseg-mblnr
AND mjahr = gs_mseg-mjahr.
IF sy-subrc = 0.
<gs_output>-welastdate = gs_mkpf-budat.
ENDIF.
ENDIF.
ENDIF.
READ TABLE gt_mseg WITH KEY bwart = '601' INTO gs_mseg.
IF sy-subrc = 0.
<gs_output>-weflag = 'X'.
SORT gt_mseg BY bwart DESCENDING mjahr DESCENDING mblnr DESCENDING.
*Ermitteln das Datum der letzten Warenausgangsbuchung
READ TABLE gt_mseg INDEX 1 INTO gs_mseg.
IF sy-subrc = 0.
SELECT SINGLE *
INTO gs_mkpf
FROM mkpf
WHERE mblnr = gs_mseg-mblnr
AND mjahr = gs_mseg-mjahr.
IF sy-subrc = 0.
<gs_output>-walastdate = gs_mkpf-budat.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
CLEAR gs_mseg.
CLEAR gt_mseg.
REFRESH gt_mseg.
CLEAR gs_mkpf.
CLEAR gt_mkpf.
REFRESH gt_mkpf.
ENDIF.
*Lesen Einkaufsinfosatz
CALL FUNCTION 'BAPI_INFORECORD_GETLIST'
EXPORTING
* VENDOR =
MATERIAL = <gs_output>-matnr
* MAT_GRP =
* VEND_MAT =
* VEND_PART =
* VEND_MATG =
PURCH_ORG = '1000'
* INFO_TYPE =
PLANT = '1000'
* PUR_GROUP =
* PURCHASINGINFOREC =
* DELETED_INFORECORDS = ' '
* PURCHORG_DATA = 'X'
* GENERAL_DATA = 'X'
* MATERIAL_EVG =
* PURCHORG_VEND = ' '
TABLES
* INFORECORD_GENERAL =
INFORECORD_PURCHORG = gt_infosatz
RETURN = gt_return
.
READ TABLE gt_return WITH KEY type = 'E' INTO gs_return.
*Einkaufsinfosatz-Flag setzen
IF sy-subrc <> 0.
IF NOT gt_infosatz[] IS INITIAL.
<gs_output>-ekinfoflag = 'X'.
ENDIF.
ENDIF.
*Abfragen, ob Orderbuch vorhanden ist
SELECT SINGLE *
INTO gs_eord
FROM eord
WHERE matnr = <gs_output>-matnr
AND werks = '1000'.
IF sy-subrc = 0.
<gs_output>-eordflag = 'X'.
ENDIF.
MODIFY gt_output INDEX output_idx FROM <gs_output> TRANSPORTING wgbez dsnam mtpos wbmng02 sum_kb nkbflag ovflag weflag waflag welastdate walastdate ekinfoflag eordflag sum_bdm_kauf sum_bdm_liefer sum_bdm_sek.
CLEAR gs_output.
[b][color=#FF4000] COMMIT WORK [/color][/b]
ENDLOOP.
Folgende Benutzer bedankten sich beim Autor ralf.wenzel für den Beitrag:
cuncon
Aber wieso nach dem ich commit work eingebaut habe, ist das time out nicht mehr aufgetreten? Das verstehe ich auch nicht ganz. Das Programm läuft auf dem Testsystem gut im Hintergrund, aber das soll auch online gut laufen könnenralf.wenzel hat geschrieben:commit work verhindert keine Laufzeitprobleme, sondern setzt sozusagen (!) den Timer auf Null zurück. Du verhinderst also nur den Abbruch, das Laufzeitproblem hast du immer noch. Ein Online-Report, der 40 Minuten läuft, läuft zu lang. Der gehört entweder in den Hintergrund oder massiv beschleunigt.
Ralf
Dafür ist es zu langsam - weil ein COMMIT WORK das Problem nicht löst.cuncon hat geschrieben:Das Programm läuft gut im Hintergrund, aber das soll auch online gut laufen können, leider.
Folgende Benutzer bedankten sich beim Autor ralf.wenzel für den Beitrag:
cuncon
Folgende Benutzer bedankten sich beim Autor DeathAndPain für den Beitrag:
cuncon
DeathAndPain hat geschrieben:Also Deinen Code finde ich beim Überfliegen erst mal nicht so verkehrt. Mein Tipp ist, dass die von Dir gerufenen SAP-Standard-Funktionsbausteine MD_STOCK_REQUIREMENTS_LIST_API und/oder BAPI_INFORECORD_GETLIST so langsam sind, dass Du bei einer hinreichend großen Anzahl Einträge einfach auf eine hohe Laufzeit kommst. Sollte ich damit richtig liegen, dann bleibt Dir nur, entweder mit der Laufzeit zu leben oder auf den Baustein zu verzichten und stattdessen selber die Daten aus der Datenbank zu lesen. Letzteres kriegt man selber eigentlich immer deutlich performanter hin als die Standard-Bausteine, wenn man weiß, in welchen Tabellen man schauen muss und wie die genauen Zusammenhänge sind, aber dafür ist man dann halt auch selbst dafür verantwortlich, dass das, was man da macht, auch inhaltlich richtig ist (auch in etwaigen Sonderfällen).
Was Du ansonsten noch tun könntest, um die Performance ein Stück nach oben zu treiben, ist, die ganzen SELECT SINGLES durch je einen großen SELECT... FOR ALL ENTRIES IN zu ersetzen und eine sortierte interne Puffertabelle damit zu füllen, aus der Du nachher in Deinen LOOPs die Daten liest. Das kostet zwar etwas Hauptspeicher, aber in einer Größenordnung, die für heutige SAP-Systeme ein Lacher ist. Du reduzierst damit die Zahl Deiner Datenbankzugriffe auf einen Bruchteil, und Datenbankzugriffe machen bei ABAP-Programmen normalerweise den allergrößten Teil der Laufzeit aus.
Womit ich an Deiner Stelle anfangen würde, ist, den wahren Laufzeitfresser zu identifizieren. Dies tust Du, indem Du in die Transaktion ST05 startest, dort auf "Trace einschalten" klickst, dann in einem anderen Modus (Fenster) Dein Programm durchlaufen lässt, dann wieder in Dein erstes Fenster wechselst, dort "Trace ausschalten" klickst und dann dort auf "Trace anzeigen" klickst. Dann bekommst Du eine Liste aller erfolgten Datenbankzugriffe. Schau Dir die verfügbaren Drucktasten und Menüeinträge an, um zu sehen, auf welche Weise Du diese Zugriffe aggregieren kannst. Finde heraus, der Zugriff auf welche Tabellen am meisten Rechenzeit verschlingt und wo diese Zugriffe angesiedelt sind. Dann siehst Du sehr schön, bei welchen Zugriffen eine Optimierung richtig was bringt.
Mit COMMIT WORK setzt Du den Timeout-Timer zurück, wie Ralf schon richtig gesagt hat. Durch immer mal wieder eingestreute COMMIT WORKs kann Dein Programm sich also in unbegrenztem Umfang Laufzeit erschleichen. Admins mögen das nur deshalb nicht, weil Timeouts einen Sinn haben und sie nicht wollen, dass marodierende Programme unbegrenzt Rechenlast erzeugen, ohne irgendwann abzubrechen. Lang laufende Berechnungsroutinen, die 40 Minuten lang volle Last erzeugen, packt man in einen Hintergrundjob, der typischerweise nachts läuft, wenn er die Anwender nicht stört. Hintergrundjobs unterliegen keinen Timeout-Beschränkungen.
Sofern es sich bei Deinem Programm um einen Report handelt, kannst Du ihn auch direkt im Hintergrund ausführen lassen, indem Du ihn nach dem Füllen des Selektionsbildes nicht mit F8 (oder Klick auf die Uhr mit grünem Haken), sondern mit F9 startest. Dann läuft er wie ein geplanter Job im Hintergrund und unterliegt keinem Timeout. Freilich siehst Du dann auch kein Ergebnisbild, weswegen Du eine Ausgabe nur der per Write-Befehl erzeugten Spooldatei (oder einer durch den Report geschriebenen Datei) entnehmen kannst.
Früher liefen im Hintergrund gestartete Programme auch etwas schneller. Ob das heute noch so ist, kann ich nicht sagen. Einen großen Unterschied gibt es da jedenfalls nicht mehr.
Folgende Benutzer bedankten sich beim Autor DeathAndPain für den Beitrag:
cuncon
Vielen Dank für den Tipp. Ich habe gerade das Programm mit F9 gestartet und mal kucken, wie das Ergebnis aussieht. Ich habe kein Write-Befehl geschrieben, sondern ALV-List.cuncon hat geschrieben:DeathAndPain hat geschrieben:Also Deinen Code finde ich beim Überfliegen erst mal nicht so verkehrt. Mein Tipp ist, dass die von Dir gerufenen SAP-Standard-Funktionsbausteine MD_STOCK_REQUIREMENTS_LIST_API und/oder BAPI_INFORECORD_GETLIST so langsam sind, dass Du bei einer hinreichend großen Anzahl Einträge einfach auf eine hohe Laufzeit kommst. Sollte ich damit richtig liegen, dann bleibt Dir nur, entweder mit der Laufzeit zu leben oder auf den Baustein zu verzichten und stattdessen selber die Daten aus der Datenbank zu lesen. Letzteres kriegt man selber eigentlich immer deutlich performanter hin als die Standard-Bausteine, wenn man weiß, in welchen Tabellen man schauen muss und wie die genauen Zusammenhänge sind, aber dafür ist man dann halt auch selbst dafür verantwortlich, dass das, was man da macht, auch inhaltlich richtig ist (auch in etwaigen Sonderfällen).
Was Du ansonsten noch tun könntest, um die Performance ein Stück nach oben zu treiben, ist, die ganzen SELECT SINGLES durch je einen großen SELECT... FOR ALL ENTRIES IN zu ersetzen und eine sortierte interne Puffertabelle damit zu füllen, aus der Du nachher in Deinen LOOPs die Daten liest. Das kostet zwar etwas Hauptspeicher, aber in einer Größenordnung, die für heutige SAP-Systeme ein Lacher ist. Du reduzierst damit die Zahl Deiner Datenbankzugriffe auf einen Bruchteil, und Datenbankzugriffe machen bei ABAP-Programmen normalerweise den allergrößten Teil der Laufzeit aus.
Womit ich an Deiner Stelle anfangen würde, ist, den wahren Laufzeitfresser zu identifizieren. Dies tust Du, indem Du in die Transaktion ST05 startest, dort auf "Trace einschalten" klickst, dann in einem anderen Modus (Fenster) Dein Programm durchlaufen lässt, dann wieder in Dein erstes Fenster wechselst, dort "Trace ausschalten" klickst und dann dort auf "Trace anzeigen" klickst. Dann bekommst Du eine Liste aller erfolgten Datenbankzugriffe. Schau Dir die verfügbaren Drucktasten und Menüeinträge an, um zu sehen, auf welche Weise Du diese Zugriffe aggregieren kannst. Finde heraus, der Zugriff auf welche Tabellen am meisten Rechenzeit verschlingt und wo diese Zugriffe angesiedelt sind. Dann siehst Du sehr schön, bei welchen Zugriffen eine Optimierung richtig was bringt.
Mit COMMIT WORK setzt Du den Timeout-Timer zurück, wie Ralf schon richtig gesagt hat. Durch immer mal wieder eingestreute COMMIT WORKs kann Dein Programm sich also in unbegrenztem Umfang Laufzeit erschleichen. Admins mögen das nur deshalb nicht, weil Timeouts einen Sinn haben und sie nicht wollen, dass marodierende Programme unbegrenzt Rechenlast erzeugen, ohne irgendwann abzubrechen. Lang laufende Berechnungsroutinen, die 40 Minuten lang volle Last erzeugen, packt man in einen Hintergrundjob, der typischerweise nachts läuft, wenn er die Anwender nicht stört. Hintergrundjobs unterliegen keinen Timeout-Beschränkungen.
Sofern es sich bei Deinem Programm um einen Report handelt, kannst Du ihn auch direkt im Hintergrund ausführen lassen, indem Du ihn nach dem Füllen des Selektionsbildes nicht mit F8 (oder Klick auf die Uhr mit grünem Haken), sondern mit F9 startest. Dann läuft er wie ein geplanter Job im Hintergrund und unterliegt keinem Timeout. Freilich siehst Du dann auch kein Ergebnisbild, weswegen Du eine Ausgabe nur der per Write-Befehl erzeugten Spooldatei (oder einer durch den Report geschriebenen Datei) entnehmen kannst.
Früher liefen im Hintergrund gestartete Programme auch etwas schneller. Ob das heute noch so ist, kann ich nicht sagen. Einen großen Unterschied gibt es da jedenfalls nicht mehr.
Das war Anforderung von meinem Chef, dass das Programm online auch gut laufen kann. Wenn das Programm nur im Hintergrund laufen muss , dann bin ich schon lange fertig damit. Ich bin auch langsam fertig wegen diesem Problem
cuncon
Folgende Benutzer bedankten sich beim Autor DeathAndPain für den Beitrag:
cuncon
Ich habe versucht das Programm mit F9 zu starten, es hat gut geklappt und ist wie Hintergrund gelaufen. Das fand ich cool, schon etwas Neues gelernt. Und zu deinem Vorschlag SAP Tabellen statt FUBA MD_STOCK_REQUIREMENTS_LIST_API und BAPI_INFORECORD_GETLIST zu verwenden , es ist gut, aber schon mühsam, weil ich mein Programm fast komplett umprogrammieren und herausfinden muss, welche Tabelle dafür geeignet sind. Wenn es nicht anders geht, muss ich das leider machenDeathAndPain hat geschrieben:Haste mal meine Tipps zur Performanceoptimierung versucht? Da ist eine Menge drin. Ich weiß ja nicht, welche Datenmengen Du mit dem Programm verarbeiten möchtest, aber von der Sache bin ich der Meinung, das muss mit einem einstelligen Minutenpensum machbar sein.
Wieso alles umprogrammieren? Du musst doch nur den FB-Aufruf durch eine Formroutine ersetzen, in der Du die entsprechenden Tabellen liest. Und wie gesagt, ich habe dunkel in Erinnerung, dass das Lesen von Einkaufsinfosätzen von der Datenbank kein Hexenwerk war.Und zu deinem Vorschlag SAP Tabellen statt FUBA MD_STOCK_REQUIREMENTS_LIST_API und BAPI_INFORECORD_GETLIST zu verwenden , es ist gut, aber schon mühsam, weil ich mein Programm fast komplett umprogrammieren und herausfinden muss, welche Tabelle dafür geeignet sind.