Allgemein
In den folgenden Beispielen werden drei der gängigen Datenbankzugriffe über ein SELECT nach Laufzeit sowie Vor- und Nachteilen verglichen.
Variante 1 (RANGE)
* Laufzeit: 0,086142s
* Vorteile:
* - Aufteilen und Manipulieren von verknüpften DB-Abfragen
* - Abfrage in einem Block -> gut für kleine Abfragen mit wenigen RANGE-Einträgen
* - Verwendung z.B. bei Nutzung von SELECT-OPTIONS
* Nachteile:
* - Laufzeitverhalten bei großen Abfragen
* - keine Abhängigkeiten von zwei oder mehr Feldern in einem RANGE abbildbar (Verknüpfung in der WHERE-Clause erfolgt nur einzeln über AND/OR)
* - RANGES werden beim SELECT in ein Statement mit vielen ORs umgewandelt
* -> abhängig vom DB-System kommt es früher oder später zu DUMPS, wenn der RANGE zu viele Einträge beinhaltet (Speichergröße der RANGE-Table > 64kB)
DATA: lv_maktx TYPE maktx VALUE '<empty>'.
DATA(o_timer) = cl_abap_runtime=>create_hr_timer( ).
DATA(usec_start) = o_timer->get_runtime( ).
* SELECT 1: Materialnummern
SELECT m~matnr, @lv_maktx AS maktx
INTO TABLE @DATA(it_mara)
FROM mara AS m
UP TO 1000 ROWS.
DATA: rg_matnr TYPE RANGE OF matnr.
* MATNR in RANGE kopieren, darauf achten, dass die Suchfelder UNIQUE sind
rg_matnr = VALUE #( FOR <m> IN it_mara
(
sign = 'I'
option = 'EQ'
low = <m>-matnr
high = ''
) ).
* SELECT 2: Kurztexte
SELECT t~matnr, t~maktx
INTO TABLE @DATA(it_makt)
FROM makt AS t
WHERE t~matnr IN @rg_matnr
AND t~spras = @sy-langu.
* Zuweisung MATNR, MAKTX
LOOP AT it_mara ASSIGNING FIELD-SYMBOL(<t>).
IF line_exists( it_makt[ matnr = <t>-matnr ] ).
<t>-maktx = it_makt[ matnr = <t>-matnr ]-maktx.
ENDIF.
ENDLOOP.
DATA(usec_end) = o_timer->get_runtime( ).
DATA(usec) = CONV decfloat16( usec_end - usec_start ).
DATA(sec) = usec / 1000000.
WRITE: / 'Laufzeit: ', sec, 's'.
cl_demo_output=>display( it_mara ).
Variante 2 (FOR ALL ENTRIES)
* Laufzeit: 0,073252s
* Vorteile:
* - Aufteilen und Manipulieren von verknüpften DB-Abfragen
* - mehrere FOR ALL ENTRIES können pro Statement verwendet werden
* - Verwendung z.B. bei Verarbeitung von vielen Datensätzen
* Nachteile:
* - Laufzeitverhalten bei großen Abfragen
* - wenn die FOR ALL ENTRIES Liste leer ist, wir die WHERE-Clause ignoriert, was zu unerwünschten Nebeneffekten führen kann
* - FOR ALL ENTRIES werden beim SELECT in viele einzelne Statements umgewandelt
* - man sollte darauf achten, dass die Einträge in der FOR ALL ENTRIES-Tabelle nur einmal auftreten -> Performance
* - Speicherverbrauch durch die vielen Abfragen
* Anmerkungen:
* - doppelte Einträge in der FOR ALL ENTRIES Tabelle werden entfernt (SELECT DISTINCT)
* - wichtige Parameter für die Performance von FOR ALL ENTRIES sind:
* rsdb/max_blocking_factor
* rsdb/min_blocking_factor
* rsdb/max_in_blocking_factor
* rsdb/min_in_blocking_factor
* rsdb/prefer_in_itab_opt
* rsdb/prefer_fix_blocking
DATA: lv_maktx TYPE maktx VALUE '<empty>'.
DATA(o_timer) = cl_abap_runtime=>create_hr_timer( ).
DATA(usec_start) = o_timer->get_runtime( ).
* SELECT 1: Materialnummern
* darauf achten, dass die Einträge in der FOR ALL ENTRIES Tabelle UNIQUE sind
SELECT m~matnr, @lv_maktx AS maktx
INTO TABLE @DATA(it_mara)
FROM mara AS m
UP TO 1000 ROWS.
* Prüfung auf IS INITIAL ist wichtig, da sonst die WHERE-Clause im SELECT ignoriert würde
IF NOT it_mara IS INITIAL.
* SELECT 2: Kurztexte
SELECT t~matnr, t~maktx
INTO TABLE @DATA(it_makt)
FROM makt AS t
FOR ALL ENTRIES IN @it_mara
WHERE t~matnr = @it_mara-matnr
AND t~spras = @sy-langu.
* Zuweisung MATNR, MAKTX
LOOP AT it_mara ASSIGNING FIELD-SYMBOL(<m>).
IF line_exists( it_makt[ matnr = <m>-matnr ] ).
<m>-maktx = it_makt[ matnr = <m>-matnr ]-maktx.
ENDIF.
ENDLOOP.
ENDIF.
DATA(usec_end) = o_timer->get_runtime( ).
DATA(usec) = CONV decfloat16( usec_end - usec_start ).
DATA(sec) = usec / 1000000.
WRITE: / 'Laufzeit: ', sec, 's'.
cl_demo_output=>display( it_mara ).
Variante 3 (JOIN)
* Laufzeit: 0,012196s * Vorteile: * - wesentlich schneller als RANGE und FOR ALL ENTRIES * - eine komplexe DB-Anfrage (generisch) * - Verwendung bei Verarbeitung von vielen Datensätzen verteilt über viele Tabellen * Nachteile: * - komplexe Statements * - schlecht zu Debuggen DATA(o_timer) = cl_abap_runtime=>create_hr_timer( ). DATA(usec_start) = o_timer->get_runtime( ). * SELECT: Materialnummern, Kurztexte SELECT m~matnr, t~maktx INTO TABLE @DATA(it_mara) FROM mara AS m RIGHT OUTER JOIN makt AS t ON ( m~matnr = t~matnr ) UP TO 1000 ROWS WHERE t~spras = @sy-langu. DATA(usec_end) = o_timer->get_runtime( ). DATA(usec) = CONV decfloat16( usec_end - usec_start ). DATA(sec) = usec / 1000000. WRITE: / 'Laufzeit: ', sec, 's'. cl_demo_output=>display( it_mara ).
Links
- http://www.sappractical.com/home/blogd/joins-vs-for-all-entries–which-performs-better/
- https://www.tricktresor.de/blog/verwendung-von-for-all-entries-2/
- http://zevolving.com/2014/03/abap-for-all-entries-why-you-need-to-include-key-fields/
- https://blogs.msdn.microsoft.com/saponsqlserver/2011/12/28/abap-select-for-all-entries-what-is-happening-in-sql-server/