Die SELECT-Anweisung ist das zentrale Werkzeug in ABAP, um Daten aus Datenbanktabellen zu lesen. Sie ist Teil von Open SQL, der datenbankagnostischen SQL-Variante in ABAP, die auf allen von SAP unterstützten Datenbanksystemen funktioniert.
Syntax
Die grundlegende Syntax bietet viele Optionen:
1. Einzelne Zeile lesen (SELECT SINGLE)
SELECT SINGLE <felder> FROM <datenbanktabelle> WHERE <bedingung> INTO <ziel>.2. Mehrere Zeilen in eine interne Tabelle lesen
SELECT <felder> FROM <datenbanktabelle> [WHERE <bedingung>] [ORDER BY <felder>] INTO TABLE <interne_tabelle>.3. Zeilenweise Verarbeitung mit Schleife
SELECT <felder> FROM <datenbanktabelle> [WHERE <bedingung>] INTO <arbeitsbereich>. " Verarbeitung jeder ZeileENDSELECT.4. Moderne Inline-Syntax (ab ABAP 7.40)
SELECT <felder> FROM <datenbanktabelle> WHERE <bedingung> INTO TABLE @DATA(lt_result).Bestandteile
<felder>: Die zu lesenden Spalten.*für alle Felder oder eine kommagetrennte Liste.FROM <datenbanktabelle>: Die Quelltabelle (oder mehrere bei JOINs).WHERE <bedingung>: Filterbedingung für die Zeilen.INTO <ziel>: Zielvariable, Struktur oder Tabelle.ORDER BY: Sortierung der Ergebnisse.GROUP BY: Gruppierung für Aggregatfunktionen.HAVING: Filter für gruppierte Ergebnisse.
Systemfelder
Nach einem SELECT werden folgende Systemfelder gesetzt:
-
sy-subrc:0: Mindestens eine Zeile gefunden.4: Keine Zeile gefunden (beiSELECT SINGLEoder leerer Ergebnismenge).
-
sy-dbcnt: Anzahl der gelesenen Zeilen.
SELECT SINGLE vs. SELECT
| Variante | Ergebnis | Anwendung |
|---|---|---|
SELECT SINGLE | Maximal eine Zeile | Zugriff über vollständigen Schlüssel |
SELECT ... INTO TABLE | Alle passenden Zeilen | Mehrere Datensätze laden |
SELECT ... ENDSELECT | Zeilenweise Verarbeitung | Legacy, vermeiden wenn möglich |
Empfehlung: Vermeiden Sie SELECT ... ENDSELECT – es ist ineffizient. Laden Sie stattdessen alle Daten in eine interne Tabelle und verarbeiten Sie diese mit LOOP AT.
Beispiele
1. SELECT SINGLE – Eine Zeile lesen
DATA: ls_customer TYPE kna1.
" Kunde über Primärschlüssel lesenSELECT SINGLE * FROM kna1 WHERE kunnr = '0000001000' INTO @ls_customer.
IF sy-subrc = 0. WRITE: / 'Kunde gefunden:', ls_customer-name1.ELSE. WRITE: / 'Kunde nicht gefunden.'.ENDIF.2. Bestimmte Felder auswählen
DATA: BEGIN OF ls_material, matnr TYPE matnr, maktx TYPE maktx, mtart TYPE mtart, END OF ls_material.
SELECT SINGLE matnr, maktx, mtart FROM mara INNER JOIN makt ON makt~matnr = mara~matnr WHERE mara~matnr = 'MAT001' AND makt~spras = @sy-langu INTO @ls_material.3. Mehrere Zeilen in eine Tabelle lesen
DATA: lt_orders TYPE TABLE OF vbak.
" Alle Aufträge eines KundenSELECT * FROM vbak WHERE kunnr = '0000001000' AND erdat >= '20250101' INTO TABLE @lt_orders.
WRITE: / 'Anzahl Aufträge:', sy-dbcnt.
LOOP AT lt_orders INTO DATA(ls_order). WRITE: / ls_order-vbeln, ls_order-erdat.ENDLOOP.4. Moderne Inline-Deklaration
" Tabelle wird automatisch mit passendem Typ erstelltSELECT matnr, maktx FROM makt WHERE spras = @sy-langu INTO TABLE @DATA(lt_materials).
" Einzelwert inlineSELECT SINGLE name1 FROM kna1 WHERE kunnr = '0000001000' INTO @DATA(lv_name).WHERE-Bedingungen
Die WHERE-Klausel unterstützt vielfältige Bedingungen:
Vergleichsoperatoren
SELECT * FROM vbak WHERE erdat = '20250101' " Gleich AND netwr > 1000 " Größer AND auart <> 'ZOR' " Ungleich INTO TABLE @DATA(lt_orders).IN-Operator (für Listen und Ranges)
" Mit WertelisteSELECT * FROM mara WHERE mtart IN ('FERT', 'HALB', 'ROH') INTO TABLE @DATA(lt_materials).
" Mit Range-Tabelle (z.B. von SELECT-OPTIONS)DATA: lr_matnr TYPE RANGE OF matnr.lr_matnr = VALUE #( ( sign = 'I' option = 'BT' low = 'A' high = 'M' ) ).
SELECT * FROM mara WHERE matnr IN @lr_matnr INTO TABLE @lt_materials.BETWEEN für Bereiche
SELECT * FROM vbak WHERE erdat BETWEEN '20250101' AND '20251231' INTO TABLE @DATA(lt_orders).LIKE für Mustersuche
" % = beliebig viele Zeichen, _ = genau ein ZeichenSELECT * FROM kna1 WHERE name1 LIKE 'Müller%' INTO TABLE @DATA(lt_customers).IS NULL / IS NOT NULL
SELECT * FROM but000 WHERE name_org1 IS NOT NULL INTO TABLE @DATA(lt_partners).JOINs – Tabellen verknüpfen
INNER JOIN
Nur Zeilen, die in beiden Tabellen existieren:
SELECT vbak~vbeln, vbak~erdat, vbap~matnr, vbap~kwmeng FROM vbak INNER JOIN vbap ON vbap~vbeln = vbak~vbeln WHERE vbak~kunnr = '0000001000' INTO TABLE @DATA(lt_order_items).LEFT OUTER JOIN
Alle Zeilen der linken Tabelle, auch ohne Treffer rechts:
SELECT mara~matnr, makt~maktx FROM mara LEFT OUTER JOIN makt ON makt~matnr = mara~matnr AND makt~spras = @sy-langu INTO TABLE @DATA(lt_materials).Mehrere JOINs
SELECT vbak~vbeln, vbak~erdat, vbap~posnr, vbap~matnr, makt~maktx FROM vbak INNER JOIN vbap ON vbap~vbeln = vbak~vbeln LEFT OUTER JOIN makt ON makt~matnr = vbap~matnr AND makt~spras = @sy-langu WHERE vbak~erdat >= '20250101' INTO TABLE @DATA(lt_result).Aggregatfunktionen
Verfügbare Funktionen
| Funktion | Beschreibung |
|---|---|
COUNT(*) | Anzahl der Zeilen |
COUNT( DISTINCT feld ) | Anzahl eindeutiger Werte |
SUM( feld ) | Summe |
AVG( feld ) | Durchschnitt |
MIN( feld ) | Minimum |
MAX( feld ) | Maximum |
Beispiel: Einfache Aggregation
" Anzahl der AufträgeSELECT COUNT(*) AS count FROM vbak WHERE kunnr = '0000001000' INTO @DATA(lv_count).
WRITE: / 'Anzahl Aufträge:', lv_count.
" Summe der AuftragswerteSELECT SUM( netwr ) AS total FROM vbak WHERE kunnr = '0000001000' INTO @DATA(lv_total).GROUP BY – Gruppierte Aggregation
" Umsatz pro KundeSELECT kunnr, SUM( netwr ) AS total_amount FROM vbak WHERE erdat >= '20250101' GROUP BY kunnr INTO TABLE @DATA(lt_customer_totals).
LOOP AT lt_customer_totals INTO DATA(ls_total). WRITE: / 'Kunde:', ls_total-kunnr, 'Umsatz:', ls_total-total_amount.ENDLOOP.HAVING – Filter nach Gruppierung
" Nur Kunden mit Umsatz > 10000SELECT kunnr, SUM( netwr ) AS total_amount FROM vbak WHERE erdat >= '20250101' GROUP BY kunnr HAVING SUM( netwr ) > 10000 INTO TABLE @DATA(lt_top_customers).ORDER BY – Sortierung
" Aufsteigend (Standard)SELECT * FROM vbak WHERE kunnr = '0000001000' ORDER BY erdat ASCENDING INTO TABLE @DATA(lt_orders_asc).
" AbsteigendSELECT * FROM vbak WHERE kunnr = '0000001000' ORDER BY erdat DESCENDING INTO TABLE @DATA(lt_orders_desc).
" Mehrere SortierfelderSELECT * FROM vbak ORDER BY kunnr ASCENDING, erdat DESCENDING INTO TABLE @DATA(lt_orders_multi).UP TO n ROWS – Ergebnismenge begrenzen
" Nur die ersten 100 ZeilenSELECT * FROM vbak WHERE erdat >= '20250101' ORDER BY erdat DESCENDING UP TO 100 ROWS INTO TABLE @DATA(lt_recent_orders).DISTINCT – Duplikate entfernen
" Alle eindeutigen AuftragsartenSELECT DISTINCT auart FROM vbak INTO TABLE @DATA(lt_order_types).FOR ALL ENTRIES – Massenabgleich mit interner Tabelle
DATA: lt_customers TYPE TABLE OF kna1.
" Erst Kunden ladenSELECT * FROM kna1 WHERE land1 = 'DE' INTO TABLE @lt_customers.
" Dann Aufträge für diese KundenIF lt_customers IS NOT INITIAL. SELECT * FROM vbak FOR ALL ENTRIES IN @lt_customers WHERE kunnr = @lt_customers-kunnr INTO TABLE @DATA(lt_orders).ENDIF.Wichtig: Prüfen Sie immer, ob die Treibertabelle (lt_customers) nicht leer ist! Bei leerer Tabelle werden alle Zeilen der Datenbanktabelle gelesen.
Subqueries
" Kunden, die Aufträge habenSELECT * FROM kna1 WHERE kunnr IN ( SELECT kunnr FROM vbak WHERE erdat >= '20250101' ) INTO TABLE @DATA(lt_active_customers).
" Aufträge über dem DurchschnittSELECT * FROM vbak WHERE netwr > ( SELECT AVG( netwr ) FROM vbak ) INTO TABLE @DATA(lt_above_avg).Escape-Zeichen (@) für Host-Variablen
In modernem ABAP Open SQL müssen ABAP-Variablen mit @ gekennzeichnet werden:
DATA: lv_customer TYPE kunnr VALUE '0000001000'.
" Modern (empfohlen)SELECT * FROM vbak WHERE kunnr = @lv_customer INTO TABLE @DATA(lt_orders).
" Legacy (ohne @) - funktioniert noch, aber veraltetSELECT * FROM vbak WHERE kunnr = lv_customer INTO TABLE lt_orders.Performance-Tipps
-
Nur benötigte Felder selektieren:
" Schlecht: Alle FelderSELECT * FROM vbak INTO TABLE @DATA(lt_all)." Besser: Nur benötigte FelderSELECT vbeln, erdat, netwr FROM vbak INTO TABLE @DATA(lt_needed). -
Indizes nutzen: Verwenden Sie WHERE-Bedingungen auf indizierten Feldern (meist Schlüsselfelder).
-
Vermeiden Sie SELECT … ENDSELECT:
" Schlecht: Viele DatenbankzugriffeSELECT * FROM vbak INTO @DATA(ls_order)." VerarbeitungENDSELECT." Besser: Ein DatenbankzugriffSELECT * FROM vbak INTO TABLE @DATA(lt_orders).LOOP AT lt_orders INTO DATA(ls_order)." VerarbeitungENDLOOP. -
UP TO n ROWS verwenden: Wenn Sie nur wenige Zeilen benötigen, begrenzen Sie die Ergebnismenge.
-
FOR ALL ENTRIES mit Vorsicht:
- Immer auf leere Treibertabelle prüfen
- Duplikate in der Treibertabelle vorher entfernen (mit
SORTundDELETE ADJACENT DUPLICATES)
-
Aggregation in der Datenbank: Berechnen Sie Summen, Durchschnitte etc. in der Datenbank statt in ABAP.
Wichtige Hinweise / Best Practice
- Prüfen Sie immer
sy-subrcnach einemSELECT SINGLEoder wenn Sie eine Zeile erwarten. - Verwenden Sie die moderne Syntax mit
@für Host-Variablen und@DATA()für Inline-Deklarationen. - Vermeiden Sie
SELECT *wenn Sie nicht alle Felder benötigen. SELECT ... ENDSELECTist ineffizient – laden Sie Daten in eine interne Tabelle und nutzen SieLOOP AT.- Bei
FOR ALL ENTRIESimmer die Treibertabelle aufIS NOT INITIALprüfen. - Nutzen Sie JOINs statt verschachtelter SELECTs für bessere Performance.
- Nach dem Laden können Sie die Daten mit
SORT,READ TABLEoderMODIFYweiterverarbeiten.