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/