ABAP SELECT Anweisung: Daten aus Datenbanktabellen lesen

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

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 Zeile
ENDSELECT.

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 (bei SELECT SINGLE oder leerer Ergebnismenge).
  • sy-dbcnt: Anzahl der gelesenen Zeilen.

SELECT SINGLE vs. SELECT

VarianteErgebnisAnwendung
SELECT SINGLEMaximal eine ZeileZugriff über vollständigen Schlüssel
SELECT ... INTO TABLEAlle passenden ZeilenMehrere Datensätze laden
SELECT ... ENDSELECTZeilenweise VerarbeitungLegacy, 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 lesen
SELECT 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 Kunden
SELECT *
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 erstellt
SELECT matnr, maktx
FROM makt
WHERE spras = @sy-langu
INTO TABLE @DATA(lt_materials).
" Einzelwert inline
SELECT 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 Werteliste
SELECT * 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 Zeichen
SELECT * 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

FunktionBeschreibung
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äge
SELECT COUNT(*) AS count
FROM vbak
WHERE kunnr = '0000001000'
INTO @DATA(lv_count).
WRITE: / 'Anzahl Aufträge:', lv_count.
" Summe der Auftragswerte
SELECT SUM( netwr ) AS total
FROM vbak
WHERE kunnr = '0000001000'
INTO @DATA(lv_total).

GROUP BY – Gruppierte Aggregation

" Umsatz pro Kunde
SELECT 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 > 10000
SELECT 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).
" Absteigend
SELECT * FROM vbak
WHERE kunnr = '0000001000'
ORDER BY erdat DESCENDING
INTO TABLE @DATA(lt_orders_desc).
" Mehrere Sortierfelder
SELECT * FROM vbak
ORDER BY kunnr ASCENDING, erdat DESCENDING
INTO TABLE @DATA(lt_orders_multi).

UP TO n ROWS – Ergebnismenge begrenzen

" Nur die ersten 100 Zeilen
SELECT * FROM vbak
WHERE erdat >= '20250101'
ORDER BY erdat DESCENDING
UP TO 100 ROWS
INTO TABLE @DATA(lt_recent_orders).

DISTINCT – Duplikate entfernen

" Alle eindeutigen Auftragsarten
SELECT 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 laden
SELECT * FROM kna1
WHERE land1 = 'DE'
INTO TABLE @lt_customers.
" Dann Aufträge für diese Kunden
IF 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 haben
SELECT * FROM kna1
WHERE kunnr IN ( SELECT kunnr FROM vbak WHERE erdat >= '20250101' )
INTO TABLE @DATA(lt_active_customers).
" Aufträge über dem Durchschnitt
SELECT * 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 veraltet
SELECT * FROM vbak
WHERE kunnr = lv_customer
INTO TABLE lt_orders.

Performance-Tipps

  1. Nur benötigte Felder selektieren:

    " Schlecht: Alle Felder
    SELECT * FROM vbak INTO TABLE @DATA(lt_all).
    " Besser: Nur benötigte Felder
    SELECT vbeln, erdat, netwr FROM vbak INTO TABLE @DATA(lt_needed).
  2. Indizes nutzen: Verwenden Sie WHERE-Bedingungen auf indizierten Feldern (meist Schlüsselfelder).

  3. Vermeiden Sie SELECT … ENDSELECT:

    " Schlecht: Viele Datenbankzugriffe
    SELECT * FROM vbak INTO @DATA(ls_order).
    " Verarbeitung
    ENDSELECT.
    " Besser: Ein Datenbankzugriff
    SELECT * FROM vbak INTO TABLE @DATA(lt_orders).
    LOOP AT lt_orders INTO DATA(ls_order).
    " Verarbeitung
    ENDLOOP.
  4. UP TO n ROWS verwenden: Wenn Sie nur wenige Zeilen benötigen, begrenzen Sie die Ergebnismenge.

  5. FOR ALL ENTRIES mit Vorsicht:

    • Immer auf leere Treibertabelle prüfen
    • Duplikate in der Treibertabelle vorher entfernen (mit SORT und DELETE ADJACENT DUPLICATES)
  6. Aggregation in der Datenbank: Berechnen Sie Summen, Durchschnitte etc. in der Datenbank statt in ABAP.

Wichtige Hinweise / Best Practice

  • Prüfen Sie immer sy-subrc nach einem SELECT SINGLE oder 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 ... ENDSELECT ist ineffizient – laden Sie Daten in eine interne Tabelle und nutzen Sie LOOP AT.
  • Bei FOR ALL ENTRIES immer die Treibertabelle auf IS NOT INITIAL prüfen.
  • Nutzen Sie JOINs statt verschachtelter SELECTs für bessere Performance.
  • Nach dem Laden können Sie die Daten mit SORT, READ TABLE oder MODIFY weiterverarbeiten.