
ABAP Quick - Ranges and Select Options
Ranges and Select Options in ABAP are very similar, yet there are subtle differences in their use in the ABAP OO context. Here we'll look at their modern usage.
Table of contents
In this article, we'll look at the basics of ranges in ABAP, compare ranges with select options, and examine their necessity in modern development.
Introduction
The foundation of every classic report in SAP is parameters and select options. Using parameters, a user can define a single value, but with select options, they can define a variety of restrictions. The advantage is that if the select option remains empty, all values are automatically drawn, or the restriction remains empty. In modern development with RAP and ABAP OO, ranges can still be valuable. Therefore, in this article, we'll look at the basics and delve into the details of their use. We will focus primarily on modern development scenarios.
To access the database, we use the ZBS_DCP_APPL table. We created this table and filled it with data in a RAP article on optimizing search helps. We will therefore also use the table and the data in this article.
Structure
In this chapter, we will look at the basics and structure of a range. We will also clarify the current differences between a range and a select option.
Range
A range always has the same structure in ABAP. This is a table with four columns (SIGN, OPTION, LOW, and HIGH), where Low and High are based on the defined data types. The data types and possible contents of the first two columns are always the same.
Select Option
The Select Option is defined in a report and belongs to the report's selection screen. Essentially, this is a range, since the table body corresponds to a range. The addition here is the additional header row that is available.
Hint: The header row is an obsolete construct and should no longer be used. Therefore, you can only define and use ranges in classes.
Standard Fields
The first two fields, SIGN and OPTION, are standardized and only allow certain values or constants. You can find more information about the permissible values in the DDSIGN and DDOPTION domains as fixed values.
Resolution
If we use a range in our code, the standard breaks the range down into a proper statement and performs a corresponding comparison. Let's take this range, for example.
DATA(filter_applications) = VALUE applications(
( sign = 'I' option = 'BT' low = 'ACC_DEF' high = 'ENGINE' )
( sign = 'E' option = 'EQ' low = 'CALC' ) ).
Now let's perform a SELECT on the data and use the range to limit the data.
SELECT FROM zbs_dcp_appl
FIELDS *
WHERE application IN @filter_applications
INTO TABLE @DATA(result_with_range).
ABAP breaks down the range into this query. We could do this manually, but using the standard function saves us a lot of time and performance. This also keeps our code flexible.
SELECT FROM zbs_dcp_appl
FIELDS *
WHERE application BETWEEN 'ACC_DEF' AND 'ENGINE'
AND NOT application = 'CALC'
INTO TABLE @DATA(result_without_range).
Usage
In this section, we'll look at its use using various examples. In these examples, however, we'll focus primarily on the modern use of Range.
Definition
You can define Range directly or as a type; the RANGE OF addition is available for this purpose. Let's first define a type that we can then use for typing or in method interfaces. In principle, we can then continue working in our code using inline declarations.
TYPES applications TYPE RANGE OF zbs_dcp_appl-application.
DATA(used_type) = VALUE applications( ( sign = 'I' option = 'EQ' low = 'CALC' ) ).
If we define a variable locally, we can only use it locally and not actually pass it to an interface.
DATA local TYPE RANGE OF zbs_dcp_appl-application.
local = VALUE #( ( sign = 'I' option = 'EQ' low = 'ENGINE' ) ).
Passing
What about passing a select option to a range? For example, we want to pass the values from a report to a class. As you saw in the structure above, with the select option, we must first address the table body and not the header row; otherwise, the compiler will notify us of the incorrect type.
range = select_option[].
Creation
In this example, we create a range directly using a SELECT and fill the result into a range, which we can then use directly for further delimitations. This saves us a lot of code for preparing the range. We use literals to create fixed values that then define the range.
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).
For it to be a valid range, LOW and HIGH must have the same type. Therefore, we double-select the Application field. Let's then look at the range in the debugger to get an idea of this method.
Delimitation
We can then use the range to define delimitation at various points in our code. We can use it to perform the usual SELECT. You can find examples in the previous sections. Or we can use a range to restrict the data in a loop.
LOOP AT databases INTO DATA(database) WHERE application IN range_filters.
" ...
ENDLOOP.
If you want to compare the contents of a field against multiple values, you can also do this using a range in an IF statement.
IF database-application IN range_filters.
" ...
ENDIF.
Parameters
What about parameters? Parameters via a selection screen or queries against individual fields in program logic are always somewhat complicated in a select. For example, if the field is empty, the select checks against the empty value, which usually no longer produces a result.
SELECT FROM zbs_dcp_appl
FIELDS *
WHERE application = @parameter
INTO TABLE @DATA(data_to_use)
The advantage of a range is that once it's empty, it's ignored in the query and all values are retrieved. To do this, we can create a local range, and once the parameter is filled, we add the entry to the range. Otherwise, it remains empty and the parameter is ignored.
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
Where does the SAP standard use ranges? Here's a prominent example of querying filters in the query class of a custom entity. Using the REQUEST object, we can return the filters as a range that the user has specified via the Fiori app.
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 this example, we return a table in which the fields and the corresponding range are stored as rows. Accordingly, in the first step, we search for the row with STAGING and then read the first row of the range to obtain the LOW value.
Complete Example
Here is the complete example from today's article. You can find the examples and uses in the various methods in the class.
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.
Conclusion
The range is still a powerful tool for data retrieval, but also for demarcation within program logic. You should now know the difference between select options and ranges, and you will be able to use ranges effectively in development in the future.
Sources:
SAP Help - TYPE RANGE OF