This is a test message to test the length of the message box.
Login
ABAP Tipp Ranges und Select-Options
Erstellt von Software-Heroes

ABAP Tipp - Ranges und Select-Options

237

Ranges und Select-Options in ABAP sind sehr ähnlich und doch gibt es feine Unterschiede bei der Nutzung im ABAP OO Kontext. Hier schauen wir uns die moderne Verwendung an.

Werbung


In diesem Artikel schauen wir uns einmal die Grundlagen von Ranges in ABAP an, vergleichen Ranges mit Select-Options und prüfen die Notwendigkeit in der modernen Entwicklung.

 

Einleitung

Die Grundlage jedes klassischen Reports in SAP sind Parameter und Select-Options. Über Parameter kann ein User nur einen Wert abgrenzen, wobei er mit Select-Options eine Vielzahl von Eingrenzungen vornehmen kann. Der Vorteil ist, wenn die Select-Option einmal leer bleibt, dann werden automatisch alle Werte gezogen bzw. bleibt die Abgrenzung leer. In der modernen Entwicklung mit RAP und ABAP OO können Ranges immer noch wertvolle Dienste leisten. Deshalb wollen wir uns in diesem Artikel die Grundlagen anschauen und in die Details bei der Nutzung gehen. Dabei werden wir vor allem auf Szenarien der modernen Entwicklung eingehen.

Für die Zugriff auf die Datenbank verwenden wir die Tabelle ZBS_DCP_APPL, diese haben wir in einem RAP Artikel zur Optimierung der Suchhilfen angelegt und mit Daten befüllt. Die Tabelle und die Daten verwenden wir deshalb auch in diesem Artikel.

 

Aufbau

In diesem Kapitel schauen wir uns die Grundlagen und den Aufbau einer Range an. Weiterhin klären wir aktuelle Unterschiede zwischen einer Range und einer Select-Option.

 

Range

Eine Range ist in ABAP immer gleich aufgebaut. Es handelt sich hierbei um eine Tabelle mit vier Spalten (SIGN, OPTION, LOW und HIGH), wobei Low und High sich nach den definierten Datentypen richten. Die Datentypen und möglichen Inhalte der ersten beiden Spalten sind immer gleich.

 

Select-Option

Die Select-Option wird in einem Report definiert und gehört zum Selektionsbild eines Reports. Im Grunde handelt es sich hier um eine Range, da der Tabellenkörper einer Range entspricht. Der Zusatz ist hier die zusätzliche Kopfzeile, die zur Verfügung steht.

 

Hinweis: Die Kopfzeile ist ein obsoletes Konstrukt und sollte nicht mehr genutzt werden. In Klassen kannst du daher nur Ranges definieren und verwenden.

 

Standardfelder

Die ersten beiden Felder SIGN und OPTION sind standardisiert und lassen nur bestimmte Werte bzw. Konstanten zu. Mehr Informationen zu den zulässigen Werten findest du in den Domänen DDSIGN und DDOPTION als Festwerte.

 

Auflösung

Verwenden wir eine Range in unserem Code, dann zerlegt der Standard die Range in ein ordentliches Statement und führt einen entsprechenden Vergleich durch. Nehmen wir zum Beispiel diese Range.

DATA(filter_applications) = VALUE applications( 
  ( sign = 'I' option = 'BT' low = 'ACC_DEF' high = 'ENGINE' )
  ( sign = 'E' option = 'EQ' low = 'CALC' ) ).

 

Führen wir nun einen SELECT auf die Daten durch und verwenden die Range zur Abgrenzung der Daten.

SELECT FROM zbs_dcp_appl
  FIELDS *
  WHERE application IN @filter_applications
  INTO TABLE @DATA(result_with_range).

 

ABAP zerlegt die Range in diese Abfrage. Grundsätzlich könnten wir das manuell machen, spart uns aber viel Zeit und Performance, wenn wir die Standardfunktion verwenden. Grundsätzlich bleibt unser Code damit auch flexibel.

SELECT FROM zbs_dcp_appl
  FIELDS *
  WHERE         application BETWEEN 'ACC_DEF' AND 'ENGINE'
        AND NOT application       = 'CALC'
  INTO TABLE @DATA(result_without_range).

 

Verwendung

In diesem Abschnitt schauen wir uns einmal die Verwendung an verschiedenen Beispielen an. Bei den Beispielen gehen wir aber vor allem auf die moderne Verwendung der Range ein.

 

Definition

Die Range kannst du direkt oder als Typ definieren, dafür steht dir der Zusatz RANGE OF zur Verfügung. Definieren wir uns im ersten Schritt einen Typen, den wir dann zur Typisierung oder in Methodenschnittstellen verwenden können. Grundsätzlich können wir dann auch per Inline-Deklaration in unserem Code weiterarbeiten.

TYPES applications TYPE RANGE OF zbs_dcp_appl-application.

DATA(used_type) = VALUE applications( ( sign = 'I' option = 'EQ' low = 'CALC' ) ).

 

Definieren wir uns eine Variable lokal, können wir diese auch nur lokal verwenden und nicht wirklich an eine Schnittstelle übergeben.

DATA local TYPE RANGE OF zbs_dcp_appl-application.

local = VALUE #( ( sign = 'I' option = 'EQ' low = 'ENGINE' ) ).

 

Übergabe

Wie sieht es eigentlich mit der Übergabe einer Select-Option an eine Range aus? Wir wollen hier zum Beispiel die Werte aus einem Report an eine Klasse übergeben. Wie du bereits oben beim Aufbau gesehen hast, müssen wir bei der Select-Option erst den Tabellenkörper ansprechen und nicht die Kopfzeile, ansonsten wird uns der Compiler auf den fehlerhaften Typ hinweisen.

range = select_option[].

 

Erstellung

In diesem Beispiel erzeugen wir uns eine Range direkt durch einen SELECT und befüllen das Ergebnis in eine Range, diese können wir dann direkt verwenden für weitere Abgrenzungen. Damit sparen wir einiges an Code, für die Vorbereitung der Range. Dabei erzeugen wir über Literale feste Werte, die die Range dann definieren.

SELECT FROM zbs_dcp_appl
  FIELDS 'I'         AS sign,
         'EQ'        AS option,
         application AS low,
         application AS high
  WHERE team = 'BASE_DEV'
  INTO TABLE @DATA(generated_range).

 

Damit es eine gültige Range wird, müssen LOW und HIGH denselben Typen haben. Daher selektieren wir das Feld Applikation doppelt. Schauen wir uns dann die Range im Debugger an, um einen Eindruck von dieser Methode zu bekommen.

 

Abgrenzung

Die Range können wir dann zur Abgrenzung an verschiedenen Stellen in unserem Code verwenden. Wir können damit den üblichen SELECT durchführen. Beispiele dazu findest du in den Abschnitten davor. Oder wir verwenden eine Range zur Einschränkung der Daten in einer Schleife.

LOOP AT databases INTO DATA(database) WHERE application IN range_filters.
  " ...
ENDLOOP.

 

Möchtest du den Inhalt eines Feldes gegen mehrere Werte abgleichen, kannst du das auch über eine Range in einem IF Statement machen.

IF database-application IN range_filters.
  " ...
ENDIF.

 

Parameter

Wie sieht es eigentlich mit Parametern aus? Parameter über ein Selektionsbild oder Abfragen gegen einzelne Felder in einer Programmlogik sind immer etwas kompliziert in einem Select. Ist das Feld zum Beispiel nicht befüllt, dann wir im Select gegen den leeren Wert geprüft, was meist zu keinem Ergebnis mehr führt.

SELECT FROM zbs_dcp_appl
  FIELDS *
  WHERE application = @parameter
  INTO TABLE @DATA(data_to_use)

 

Der Vorteil einer Range ist, wenn sie einmal leer ist, dann wird sie in der Abfrage ignoriert und alle Werte gezogen. Dazu können wir uns eine lokale Range anlegen und wenn der Parameter befüllt ist, übernehmen wir den Eintrag in die Range. Ansonsten bleibt sie leer und der Parameter wird ignoriert.

DATA optional_filter TYPE applications.

IF parameter IS NOT INITIAL.
  INSERT VALUE #( sign   = 'I'
                  option = 'EQ'
                  low    = parameter ) INTO TABLE optional_filter.
ENDIF.

SELECT FROM zbs_dcp_appl
  FIELDS *
  WHERE application IN @optional_filter
  INTO TABLE @DATA(data_to_use).

 

Standard

Wo verwendet überall der SAP Standard Ranges? Dazu ein prominentes Beispiel zur Abfrage der Filter in der Query Klasse einer Custom Entity. Über das REQUEST Objekt können wir uns die Filter als Range zurückgeben lassen, die der Anwender über die Fiori App vorgegeben hat.

TRY.
    DATA(filters) = request->get_filter( )->get_as_ranges( ).
    staging = filters[ name = `STAGING` ]-range[ 1 ]-low.
  CATCH cx_rap_query_filter_no_range.
    staging = 'TEST'.
ENDTRY.

 

In diesem Beispiel lassen wir uns eine Tabelle zurückgeben, in der die Felder und die dazugehörige Range als Zeilen abgelegt sind. Entsprechend suchen wir im ersten Schritt nach der Zeile mit STAGING und lesen dann die erste Zeile der Range, um den LOW Wert zu übernehmen.

 

Vollständiges Beispiel

Hier noch das komplette Beispiel aus dem heutigen Artikel. In der Klasse findest du in den verschiedenen Methoden die Beispiele und Verwendungen.

CLASS zcl_bs_demo_ranges_and_options DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PRIVATE SECTION.
    TYPES applications TYPE RANGE OF zbs_dcp_appl-application.

    METHODS assignment
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS create_via_select
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS definition
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS technical_usage
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS delimitation
      IMPORTING !out TYPE REF TO if_oo_adt_classrun_out.

    METHODS use_with_parameter
      IMPORTING !out       TYPE REF TO if_oo_adt_classrun_out
                !parameter TYPE zbs_dcp_appl-application.
ENDCLASS.


CLASS zcl_bs_demo_ranges_and_options IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    technical_usage( out ).
    definition( out ).
    assignment( out ).
    create_via_select( out ).
    delimitation( out ).
    use_with_parameter( out       = out
                        parameter = 'GENERAL' ).
    use_with_parameter( out       = out
                        parameter = '' ).
  ENDMETHOD.


  METHOD technical_usage.
    DATA(filter_applications) = VALUE applications( ( sign = 'I' option = 'BT' low = 'ACC_DEF' high = 'ENGINE' )
                                                    ( sign = 'E' option = 'EQ' low = 'CALC' ) ).

    SELECT FROM zbs_dcp_appl
      FIELDS *
      WHERE application IN @filter_applications
      INTO TABLE @DATA(result_with_range).

    out->write( result_with_range ).

    SELECT FROM zbs_dcp_appl
      FIELDS *
      WHERE         application BETWEEN 'ACC_DEF' AND 'ENGINE'
            AND NOT application       = 'CALC'
      INTO TABLE @DATA(result_without_range).

    out->write( result_without_range ).
  ENDMETHOD.


  METHOD definition.
    DATA local TYPE RANGE OF zbs_dcp_appl-application.
    local = VALUE #( ( sign = 'I' option = 'EQ' low = 'ENGINE' ) ).
    
    DATA(used_type) = VALUE applications( ( sign = 'I' option = 'EQ' low = 'CALC' ) ).
  ENDMETHOD.


  METHOD assignment.
*    range = select_option[].
  ENDMETHOD.


  METHOD create_via_select.
    SELECT FROM zbs_dcp_appl
      FIELDS 'I'         AS sign,
             'EQ'        AS option,
             application AS low,
             application AS high
      WHERE team = 'BASE_DEV'
      INTO TABLE @DATA(generated_range).

    out->write( generated_range ).

    SELECT FROM zbs_dcp_appl
      FIELDS *
      WHERE application IN @generated_range
      INTO TABLE @DATA(used_range).

    out->write( used_range ).
  ENDMETHOD.


  METHOD delimitation.
    DATA(range_filters) = VALUE applications( ( sign = 'I' option = 'EQ' low = 'CALC' ) ).

    SELECT FROM zbs_dcp_appl
      FIELDS *
      INTO TABLE @DATA(databases).

    LOOP AT databases INTO DATA(database) WHERE application IN range_filters.
      out->write( database ).
    ENDLOOP.

    IF database-application IN range_filters.
      out->write( database ).
    ENDIF.
  ENDMETHOD.


  METHOD use_with_parameter.
    DATA optional_filter TYPE applications.

    IF parameter IS NOT INITIAL.
      INSERT VALUE #( sign   = 'I'
                      option = 'EQ'
                      low    = parameter ) INTO TABLE optional_filter.
    ENDIF.

*    SELECT FROM zbs_dcp_appl
*      FIELDS *
*      WHERE application = @parameter
*      INTO TABLE @DATA(data_to_use).

    SELECT FROM zbs_dcp_appl
      FIELDS *
      WHERE application IN @optional_filter
      INTO TABLE @DATA(data_to_use).

    out->write( data_to_use ).
  ENDMETHOD.
ENDCLASS.

 

Fazit

Die Range ist nach wir vor ein mächtiges Werkzeug bei der Datenbeschaffung, aber auch zur Abgrenzung innerhalb der Programmlogik. Der Unterschied zwischen Select-Option und Range sollte dir nun bekannt sein und du kannst in Zukunft Ranges effektiv in der Entwicklung einsetzen.

 

Quellen:
SAP Help - TYPE RANGE OF


Enthaltene Themen:
TippRangeSelect-Option
Kommentare (3)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


Recycling-Heroes (Erklärt)

Kategorie - ABAP

Was haben die Recycling-Heroes mit moderner ABAP Entwicklung und ABAP Cloud zu tun? In diesem Artikel geben wir Einblicke in die Idee.

15.07.2025

ABAP Tipp - Generische Query Implementierung

Kategorie - ABAP

Die immer wieder gleiche Implementierung in Query Klassen für Custom Entities in RAP? Es wird mal Zeit für eine wiederverwendebare Komponente.

22.04.2025

ABAP in Praxis - String Verarbeitung

Kategorie - ABAP

In diesem praktischen Beispiel schauen wir uns die String Verarbeitung zur Ermittlung der CDS Namen in CamelCase an und wie du das mit ABAP umsetzen kannst.

15.10.2024

ABAP in der Praxis - Test Driven Development

Kategorie - ABAP

Wie funktioniert eigentlich TDD in der Praxis und gibt es einfache Beispiele zum Lernen in ABAP? In dieser Übung gehen wir auf den praktischen Teil ein.

24.09.2024

ABAP in der Praxis - Datenmenge zusammenführen

Kategorie - ABAP

Wir führen wir zwei unterschiedliche Datenmengen in ABAP zusammen, vor allem im Hinblick auf das Moderne ABAP? Eine praktische Aufgabe zum Thema.

17.09.2024