| Syntax | Notes |
| `... INTO TABLE ...` | An internal table is expected for the result set. It may be declared inline to automatically have a suitable type, which also applies to `... INTO ...`. ```abap SELECT FROM dbtab FIELDS * WHERE ... INTO TABLE @itab. SELECT FROM dbtab FIELDS * WHERE ... INTO TABLE @DATA(itab_inl). ``` |
| `... INTO ...` | Expects a structure when used without `TABLE`. ```abap SELECT SINGLE comp1, comp2, comp3 FROM dbtab WHERE ... INTO @struc. SELECT SINGLE comp1, comp2, comp3 FROM dbtab WHERE ... INTO @DATA(struc_inl). ``` |
| `... INTO CORRESPONDING FIELDS OF [TABLE] ...` | - Only the content of columns for which there are identically named components in the target are assigned. - However, if you want to read data into an existing data object and particular fields are specified in the `SELECT` list or `FIELDS` clause, and if the addition is **not** specified, you might stumble on undesired results. - The target data object must contain enough components and the content of the columns are assigned to the components of the target from left to right in the order specified. The content of surplus components of the target is not changed. - Plus, pay attention to [assignment rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_into_conversion.htm). - Basic rule: Without `CORRESPONDING ...`, column names do not play a role but only the position. With `CORRESPONDING ...`, the position of the columns does not play a role but only the name. ```abap SELECT SINGLE comp1, comp2, comp3 FROM dbtab WHERE ... INTO CORRESPONDING FIELDS OF @struc. SELECT FROM dbtab FIELDS comp1, comp2, comp3 WHERE ... INTO CORRESPONDING FIELDS OF TABLE @itab. ``` |
| `... APPENDING [CORRESPONDING FIELDS OF] TABLE ...` | The addition `INTO` initializes the target object. When using the addition `APPENDING`, you can retain existing lines in internal tables. `APPENDING` is also possible with the addition `CORRESPONDING FIELDS OF`. ```abap SELECT * FROM dbtab WHERE ... APPENDING TABLE @itab. "APPENDING is also possible with the addition CORRESPONDING FIELDS OF SELECT * FROM dbtab WHERE ... APPENDING CORRESPONDING FIELDS OF TABLE @diff_itab. ``` |
NEW addition: Specifying an anonymous data object as target object |
- Specifying an [anonymous data object](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenanonymous_data_object_glosry.htm) as target object using the addition [`NEW`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_into_target.htm#!ABAP_ALTERNATIVE_3@3@)
- Only to be used after `INTO` and not `APPENDING`.
``` abap "The examples declares target objects as anonymous data objects inline. SELECT * FROM zdemo_abap_flsch INTO TABLE NEW @DATA(itab_ref). SELECT SINGLE * FROM zdemo_abap_flsch INTO NEW @DATA(wa_ref). ``` |
| `... INTO ( dobj1, dob2, ... ) ...` | Apart from reading into structures and internal tables outlined above, you can also read into individual elementary data objects. Here, the individual elementary data objects as target objects are specified in a comma-separated list (e. g. as existing host variables or declared inline) and put between a pair of parentheses. Note: - The comma-separated list must have the same number of elements as columns in the result set. - The content of the columns in the result set is assigned to the data objects specified in the list from left to right in accordance with the order specified in the `SELECT` list. - Note the [assignment rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenselect_into_conversion.htm) also in this context. - More information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinto_clause.htm#!ABAP_ALTERNATIVE_1@1@). ``` abap "Elementary data objects as target data objects DATA id TYPE zdemo_abap_carr-carrid. DATA name TYPE zdemo_abap_carr-carrname. SELECT SINGLE FROM zdemo_abap_carr FIELDS carrid, carrname WHERE carrid = char`LH` INTO ( @id, @name ). "Inline declarations with DATA/FINAL and the "creation of anonymous data objects with NEW are "possible SELECT SINGLE FROM zdemo_abap_carr FIELDS carrid, carrname, url WHERE carrid = char`LH` INTO ( @id, @DATA(name2), NEW @FINAL(dref) ). ``` |
| Subject | Details/Code Snippet |
| `SINGLE` addition: Retrieving a single row into a structure | ``` abap "SINGLE addition "Here, all fields of a single row a read. Specifying an "asterisk * indicates that all fields are to be read. "Alternatively, you can list all the fields separated by comma. "Note that if the selection covers more than one row, e. g. in case "of a non-unique WHERE clause, one of these rows is included in "the result. SELECT SINGLE FROM dbtab FIELDS * WHERE ... INTO @struc. "Existing structure of dbtab's row type "Retrieving a selected set of fields of a single row SELECT SINGLE FROM dbtab FIELDS comp1, comp2, comp3 WHERE ... INTO @DATA(struc2). "Structure declared inline "Alternative syntax without the FIELDS addition "Here, the CORRESPONDING FIELDS OF addition is used. Only the content of "columns that have identically named components in the target data object "is assigned. SELECT SINGLE comp1, comp2, comp3 "Selected set of fields FROM dbtab WHERE ... INTO CORRESPONDING FIELDS OF @struc. "Existing structure ``` |
| Retrieving multiple rows into an internal table | ``` abap SELECT FROM dbtab FIELDS * "All fields WHERE ... INTO TABLE @itab. "itab has an appropriate row type "Alternative syntax without the FIELDS addition SELECT comp1, comp2, comp3 "Selected set of fields FROM dbtab WHERE ... INTO TABLE @DATA(lv_itab). "Internal table declared inline "Selected set of fields, existing variable "See the note on CORRESPONDING FIELDS OF above SELECT FROM dbtab FIELDS comp1, comp2, comp3 "Selected set of fields WHERE ... INTO CORRESPONDING FIELDS OF TABLE @itab. "The addition INTO initializes the target object. When using the addition APPENDING, "you can retain existing lines in internal tables. SELECT * FROM dbtab WHERE ... APPENDING TABLE @itab. "APPENDING is also possible with the addition CORRESPONDING FIELDS OF SELECT * FROM dbtab WHERE ... APPENDING CORRESPONDING FIELDS OF TABLE @diff_itab. ``` |
SELECT loop: Sequentially retrieving multiple rows |
- A `SELECT` loop can be opened if the assignment is made to a structure and the addition `SINGLE` is not used.
- If the row is found, the system field `sy-subrc` is set to `0`.
- The loop must be closed using `ENDSELECT`.
- To terminate the loop completely, you can use the statement [`EXIT`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapexit_loop.htm).
- Note: As covered further down, when using the addition `PACKAGE SIZE` and storing the result in a table, a loop is opened, too.
``` abap SELECT FROM dbtab FIELDS * WHERE ... INTO @struc. IF sy-subrc = 0. ... "For example, making changes on data and adding the row to an internal table. ENDIF. ENDSELECT. ``` |
| Checking the existence of a row in a database table | ``` abap "The example uses @abap_true. Other specifications are possible, e.g. 'X'. SELECT SINGLE @abap_true FROM dbtab WHERE ... INTO @DATA(exists). IF exists = abap_true. ... ENDIF. ``` |
DISTINCT addition: Removing rows that occur more than once in a multirow result set |
- Cannot be used with the addition `SINGLE`.
- See more information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_clause.htm).
``` abap "The following example assumes that there are multiple flights "from a city of the specified carrier with the particular destination. "Assume there are 5 flights available from Frankfurt (cityfrom) that "matches the WHERE clause. The first statement only returns one entry "in the internal table, the second 5 (all). "Using the DISTINCT addition SELECT DISTINCT cityfrom FROM zdemo_abap_flsch WHERE carrid = 'LH' AND cityto = 'NEW YORK' INTO TABLE @DATA(itab_distinct). "Not using the DISTINCT addition SELECT cityfrom FROM zdemo_abap_flsch WHERE carrid = 'LH' AND cityto = 'NEW YORK' INTO TABLE @DATA(itab_no_distinct). ``` |
UP TO n ROWS addition: Limiting the number of returned table rows |
[`UP TO n ROWS`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_up_to_offset.htm)
``` abap "A maximum of five rows are to be returned "If the INTO clause is the last clause, the UP TO clause must be positioned after it. SELECT * FROM dbtab WHERE ... INTO TABLE @DATA(itab_upto) UP TO 5 ROWS. ``` |
OFFSET n addition: Returning only the table rows after a row with a specified count from the result set |
- [`OFFSET n`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_up_to_offset.htm#!ABAP_ADDITION_2@2@)
- You can only use the addition, if an `ORDER BY` clause is specified.
``` abap "In the example, data of all flights are retrieved, except for the 2 flights "with the shortest flight time. SELECT * FROM ztest_abap_flsch WHERE carrid = 'LH' ORDER BY fltime ASCENDING INTO TABLE @DATA(itab) OFFSET 2. ``` |
PACKAGE SIZE n addition: Storing the result in packages of a specified number of rows |
The addition [`PACKAGE SIZE n`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapinto_clause.htm#!ABAP_ONE_ADD@1@) can be specified after `INTO TABLE` and `APPENDING TABLE`. A `SELECT` loop ist opened. After `PACKAGE SIZE`, the number of rows is specified (which can be a host variable, host expression or a literal of type `i`) denoting the number of rows to be inserted in the target object per iteration.
``` abap SELECT FROM dbtab FIELDS comp1, comp2, comp3 WHERE ... INTO TABLE @DATA(itab_pack) PACKAGE SIZE n. ... ENDSELECT. ``` |
INDICATORS [NOT] NULL STRUCTURE addition: Specifying null indicators |
- The `INDICATORS ...` addition is used to specify indicators such as the [null indicator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennull_indicator_glosry.htm) and store information about which columns of the result set contain the null value and which do not.
- In the example, an appropriate target table is defined to also store information about which columns of the result set contain the null value and which do not.
- More syntax options are available for `INDICATORS ...`. Find more information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_indicators.htm).
``` abap "The following example uses a left outer join to intentionally create null values. For "this purpose, two demo database tables of the cheat sheet repository are cleared and "populated with specific values to visualize null values. DELETE FROM zdemo_abap_tab1. DELETE FROM zdemo_abap_tab2. MODIFY zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 1 char1 = 'a' char2 = 'y' ) ( key_field = 2 char1 = 'b' char2 = 'z' ) ) ). MODIFY zdemo_abap_tab2 FROM TABLE @( VALUE #( ( key_field = 1 char1 = 'a' ) ( key_field = 2 char1 = 'a' ) ( key_field = 3 char1 = 'b' ) ( key_field = 4 ) ) ). "Note that for the entry 'key_field = 4' no char1 value was passed. "char1 is a shared column of the two database tables, and which is used in "the ON condition of the join. Since there is no entry in char1 for 'key_field = 4', "the joined values are null in that case. "The example visualizes the null values. The INDICATORS addition is used to specify "indicators such as the null indicator. In the example, an appropriate target table "is defined to also store information about which columns of the result set contain "the null value and which do not. TYPES: BEGIN OF st4null, BEGIN OF s2, key_field TYPE zdemo_abap_tab2-key_field, char2 TYPE zdemo_abap_tab1-char2, END OF s2, BEGIN OF nulls, key_field TYPE c LENGTH 1, char2 TYPE c LENGTH 1, END OF nulls, END OF st4null. DATA joined_tab_w_null_ind TYPE TABLE OF st4null WITH EMPTY KEY. SELECT tab2~key_field, tab1~char2 FROM zdemo_abap_tab2 AS tab2 LEFT OUTER JOIN zdemo_abap_tab1 AS tab1 ON tab1~char1 = tab2~char1 INTO TABLE @joined_tab_w_null_ind INDICATORS NULL STRUCTURE nulls. *Internal table content: *S2 NULLS *KEY_FIELD CHAR2 KEY_FIELD CHAR2 *1 y *KEY_FIELD CHAR2 KEY_FIELD CHAR2 *2 y *KEY_FIELD CHAR2 KEY_FIELD CHAR2 *3 z *KEY_FIELD CHAR2 KEY_FIELD CHAR2 *4 X ``` |
| Clause | Details/Code Snippet |
GROUP BY |
[`GROUP BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapgroupby_clause.htm)
clause: Combining groups of table rows in the result set. You
can also use [SQL expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_expression_glosry.htm "Glossary Entry")
here. Multiple clause elements are separated by a comma. Find more information and syntax options in the [ABAP Keyword Documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapgroupby_clause.htm).
Note that the `GROUP BY` clause requires all columns that are
directly specified in the `SELECT` list or specified there as an
argument of an SQL expression to be specified. An exception to this is
[aggregate
functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenaggregate_function_glosry.htm "Glossary Entry")
in [aggregate
expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenaggregate_expression_glosry.htm "Glossary Entry")
(except [grouping
functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abengrouping_glosry.htm "Glossary Entry"))
as shown in the following example.
In the example below, the database table rows that have the same content in column `comp1` are combined. The lowest and highest values in column `comp2` are determined for each of these groups and placed into the combined row.
``` abap SELECT FROM dbtab FIELDS comp1, MIN( comp2 ) AS min, MAX( comp2 ) AS max WHERE ... GROUP BY comp1 INTO ... ``` |
HAVING |
[`HAVING`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaphaving_clause.htm)
clause: Limiting the number of table rows in groups in the
result by setting conditions on these rows. The rows for which a
logical expression is true are inserted in the target variable. Note
that `HAVING` can only be used together with `GROUP BY`.
``` abap SELECT FROM dbtab FIELDS comp1, MIN( comp2 ) AS min, MAX( comp3 ) AS max WHERE ... GROUP BY comp1 HAVING comp1 LIKE '%XYZ%' AND SUM( comp4 ) > 100 INTO ... ``` |
ORDER BY |
[`ORDER BY`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaporderby_clause.htm)
clause: Sorting the result set by specified columns.
The following example shows the ordering of the result set based on the
content of the primary key of the [data source](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abendata_source_glosry.htm "Glossary Entry").
You can also order by any columns and by explicitly specifying the sort order. There are more ordering options, for example, by using SQL expressions. Find more information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaporderby_clause.htm).
> [!NOTE]
>- Not specifying `ORDER BY` means that the order of entries in the result set is undefined.
>- If `ORDER BY` and `GROUP BY` clauses are used, all columns specified after `ORDER BY` must also be specified after `GROUP BY`.
>- If aggregate functions are specified after `SELECT`, all columns that are specified after `ORDER BY` and that do not have an alias name for an aggregate function must also be specified after `SELECT` and after the `GROUP BY` clause which is required in this case, too.
``` abap SELECT FROM dbtab FIELDS comp1, comp2, comp3 WHERE ... ORDER BY PRIMARY KEY "comp2 ASCENDING "comp2 DESCENDING INTO ... ``` |
WHERE |
[`WHERE`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapwhere.htm) clause: Restricts the number of rows that are included in the result set using logical expressions. See further information on them in the following sections.
``` abap SELECT FROM dbtab FIELDS comp1, comp2, comp3 WHERE comp1 = 'abc' AND comp2 < 123 INTO ... ``` |
| Subject | Details/Code Snippet |
| Using an inner join |
[Inner join](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abeninner_join_glosry.htm):
- Columns of two or more data sources in a result set can be joined.
- Result set:
- Columns of the rows in the result set of the left side with the columns of the rows in the result set of the right side are joined into a single result set.
- Contains all combinations of rows for whose columns the join condition is true.
- If there are identical column names in multiple data sources, use the [column
selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_comp_selector_glosry.htm "Glossary Entry")
`~`.
``` abap SELECT a~comp1, a~comp2, b~comp3, c~comp4 FROM dbtab1 AS a INNER JOIN dbtab2 AS b ON a~comp1 = b~comp1 AND a~comp2 = b~comp2 INNER JOIN dbtab3 AS c ON a~comp1 = c~comp1 WHERE ... INTO ... ``` |
| Using an outer join |
[Outer join](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenouter_join_glosry.htm):
- Realized by either a [left outer join](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenleft_outer_join_glosry.htm) or
a [right outer join](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenright_outer_join_glosry.htm).
- Result set:
- Same result set as the inner join.
- Difference: For each selected row on the left side as `LEFT OUTER JOIN` or on the right side as `RIGHT OUTER JOIN`, at least one row is created in the result set even if no rows on the other side meet the condition. The columns on the other side that do not meet the condition are filled with [null values](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennull_value_glosry.htm).
``` abap "Example for a left outer join SELECT a~comp1, a~comp2, b~comp3, FROM dbtab1 AS a LEFT OUTER JOIN dbtab2 AS b ON a~comp1 = b~comp1 WHERE ... INTO ... ``` |
| Merging the result sets of multiple queries into a single result set |
... using the [set operator](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_set_operators_glosry.htm) [`UNION`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapunion.htm#!ABAP_VARIANT_1@1@). In this case, the rows of the result set of the query after `UNION` are inserted into the result set of the query in front of `UNION`.
``` abap SELECT FROM dbtab1 FIELDS ... WHERE ... UNION SELECT FROM dbtab2 FIELDS ... WHERE ... INTO ... ``` |
| Returning distinct rows of a result set (1) |
... of a query specified before the `INTERSECT` addition that are also available in the result set of the query after the `INTERSECT` addition.
``` abap "If you have imported the cheat sheet repository and already run an example class to fill "the demo tables, you can check the contents of the result sets. SELECT zdemo_abap_flsch~carrid, zdemo_abap_carr~carrname FROM zdemo_abap_flsch INNER JOIN zdemo_abap_carr ON zdemo_abap_carr~carrid = zdemo_abap_flsch~carrid ORDER BY zdemo_abap_flsch~carrid INTO TABLE @DATA(itab_no_intersect). "Using INTERSECT; the result set contains distinct rows SELECT zdemo_abap_flsch~carrid, zdemo_abap_carr~carrname FROM zdemo_abap_flsch INNER JOIN zdemo_abap_carr ON zdemo_abap_carr~carrid = zdemo_abap_flsch~carrid INTERSECT SELECT carrid, carrname FROM zdemo_abap_carr ORDER BY carrid INTO TABLE @DATA(itab_w_intersect). ``` |
| Returning distinct rows of a result set (2) |
... of a query specified before the `EXCEPT` addition that are not available in the result set of the query after the `EXCEPT` addition.
```abap "If you have imported the cheat sheet repository and already run an example class to fill "the demo tables, you can check the contents of the result sets. "Selecting all carrier IDs from a database table that do not exist in an "internal table TYPES: ty_demo_tab TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY. DATA(itab) = VALUE ty_demo_tab( ( carrid = 'LH' ) ( carrid = 'LH' ) ( carrid = 'LH' ) ( carrid = 'AA' ) ( carrid = 'AA' ) ). "Selecting all carrier IDs for comparison SELECT carrid FROM zdemo_abap_carr INTO TABLE @DATA(all_carrids). "Using EXCEPT; the result set excludes those carrier IDs present in the "internal table SELECT carrid FROM zdemo_abap_carr EXCEPT SELECT it~carrid FROM @itab AS it INNER JOIN zdemo_abap_carr ON zdemo_abap_carr~carrid = it~carrid ORDER BY carrid ASCENDING INTO TABLE @DATA(itab_w_except). ``` |
char\`abc\`) with [built-in ABAP Dictionary
types](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddic_builtin_types.htm)
or untyped. See the [Typed Literals](#typed-literals) section further down.
- Regarding host expressions: Structures and internal tables are
possible as host expressions for statements modifying the
content of database tables as shown further down.
- Find more information
[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_operands.htm).
> [!NOTE]
> Questions about when to use what, what is possible in which contexts and positions, is beyond the scope of this cheat sheet. Check the details in the
respective topics in the ABAP Keyword Documentation. Find a general overview of important operand positions in ABAP SQL [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_operand_positions_oview.htm). Due to the rich variety of options, the cheat sheet covers a selection.
Example demonstrating possible operands:
``` abap
DATA number TYPE i VALUE 3.
SELECT FROM zdemo_abap_flsch
FIELDS
"Specifies a column of a data source directly using its name
cityfrom,
"Column selector ~ can be used to prefix every specified column.
"Here, it is optional. It is non-optional, e. g., if multiple data
"sources in an ABAP SQL statement are edited and the column name
"is not unique.
zdemo_abap_flsch~cityto,
'Lufthansa' AS name, "Untyped literal
char`X` AS flag, "Typed literal
@number AS num, "Host variable
@( cl_abap_context_info=>get_system_date( ) ) as date "Host expression
WHERE carrid = 'LH' "Untyped literal
AND countryfr = char`DE` "Typed literal
"Data object created inline and escaped with @
INTO TABLE @DATA(it)
"The following clause shows all options having the same effect
UP TO 3 ROWS. "Untyped numeric literal
"UP TO int4`3` ROWS. "Typed numeric literal
"UP TO @number ROWS. "Host variable
"UP TO @( 10 - 7 ) ROWS. "Host expression
```
## SQL Expressions
- SQL expressions can be specified in various positions of ABAP SQL statements.
- They are passed to the database system for evaluation.
- For example, SQL expressions can be specified as columns in the
`SELECT` list as demonstrated in most of the following examples.
- Find information on more possible positions and general information
on SQL expressions
[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapsql_expr.htm)
and the subtopics there.
> [!NOTE]
> You can [enclose SQL expressions in parentheses](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_exp_parentheses.htm).
> ```abap
> SELECT SINGLE
> carrid,
>
> "SQL expressions enclosed in parenthesis in the context
> "of arithmetic expressions impacting the prioriry of
> "calculations
> ( 1 + 2 ) * ( 3 + 4 ) * ( 5 + 6 ) AS calc_w_par, "231
> 1 + 2 * 3 + 4 * 5 + 6 AS calc_no_par "33
>
> FROM zdemo_abap_fli
> WHERE carrid = 'AA'
> INTO @DATA(using_parentheses).
> ```
### Elementary SQL Expressions
- An elementary expression represents one of the four mentioned
operands above: A value from the data source (i.e. the column name), values from an ABAP program passed to the database (literal, host
variable or host expression).
- As an example, see the `SELECT` list in the example above.
- See more information
[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_elem.htm).
### Arithmetic Expressions
[Arithmetic expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_arith.htm) perform arithmetic calculations using the operators `+`, `-`, `*`, `/`.
``` abap
SELECT SINGLE
carrid,
"Arithmethic expressions
"operators + - *
"Note that / is not allowed in integer expressions as the one below
( 1 + 2 ) * 3 AS calc,
"/ used in an expression using type adjustment in ABAP SQL.
"A cast expression converts the value of the operands to the
"specified dictionary type. The result is a representation of the
"source value in the specified type.
CAST( 1 AS D34N ) / CAST( 2 AS D34N ) AS ratio
FROM zdemo_abap_carr
WHERE carrid = 'AA'
INTO @DATA(arithmetic_sql_expr).
```
### Cast Expressions
- [Cast expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_cast.htm) are used to convert the value of operands to a dedicated dictionary type.
- Note that there are special [conversion rules](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_cast_rules.htm).
- See the possible types in the [ABAP Keyword Documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_cast.htm).
```abap
SELECT SINGLE
carrid,
"A cast expression converts the value of the operands to the
"specified dictionary type. The result is a representation of the
"source value in the specified type.
CAST( 1 AS D34N ) / CAST( 2 AS D34N ) AS ratio,
CAST( connid AS INT4 ) AS connidnum,
CAST( @( cl_abap_context_info=>get_system_date( ) ) AS CHAR ) AS dat
FROM zdemo_abap_fli
WHERE carrid = 'AA'
INTO @DATA(cast_expr).
```
### String Expressions
[String expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_string.htm) use the operator `&&` to concatenate character strings.
```abap
SELECT SINGLE
carrid,
"String expression using && to concatenate two character strings;
"the result of the concatenation must not be longer than
"255 characters.
carrid && char`_` && carrname AS concat
FROM zdemo_abap_carr
WHERE carrid = 'AA'
INTO @DATA(string_expr).
```
### Case Expressions
- [Case expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_case.htm) carry out either a simple (comparison of the values of a dedicated operand) or complex (searched case; evaluation of multiple logical expressions) case distinction.
- Not specifying `ELSE` means that the result is the [null value](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennull_value_glosry.htm). The null value can be specified explicitly by the null expression `NULL`.
```abap
SELECT SINGLE
carrid,
"Simple case distinction
"The expression compares the values of an operand with other
"operands. Result: The first operand after THEN for which the
"comparison is true. If no matches are found, the result specified
"after ELSE is selected.
CASE currcode
WHEN 'EUR' THEN 'A'
WHEN 'USD' THEN 'B'
ELSE 'C'
END AS case_simple,
"Complex case distinction
"The expression evaluates logical expressions. Result: The first
"operand after THEN for which the logical expression is true. If no
"logical expressions are true, the result specified after ELSE is
"selected.
CASE WHEN length( carrname ) <= 5 THEN 'small'
WHEN length( carrname ) BETWEEN 6 AND 10 THEN 'mid'
WHEN length( carrname ) BETWEEN 11 AND 15 THEN 'large'
ELSE 'huge'
END AS case_complex
FROM zdemo_abap_carr
WHERE carrid = 'AA'
INTO @DATA(case_expr).
```
### Aggregate Expressions
- [Aggregate expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_aggregate.htm) consist of [aggregate functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenaggregate_function_glosry.htm "Glossary Entry")
and aggregate the values of multiple rows of the result set of a
[query](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenquery_glosry.htm "Glossary Entry")
into a single value
- The example shows a selection. Find more information
[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_aggregate.htm).
Example:
``` abap
"The example shows a selection of available functions
SELECT
carrid,
"Average value of the content of a column in a row set
AVG( fltime ) AS fltime1,
"AVG with data type specification for the result
AVG( fltime AS DEC( 14,4 ) ) AS fltime2,
"Maximum value of the results in a row set
MAX( fltime ) AS max,
"Minimum value
MIN( fltime ) AS min,
"Sum of the results in a row set.
SUM( fltime ) AS sum,
"Returns the number of rows in a row set.
"The following two have the same meaning.
COUNT( * ) AS count2,
COUNT(*) AS count3,
"Chains the results in a row set.
"An optional separator can be specified
STRING_AGG( airpfrom, ', ' ) AS string_agg
FROM zdemo_abap_flsch
WHERE carrid = 'LH'
GROUP BY carrid
INTO TABLE @DATA(agg_exp).
```
### Window Expressions
How [window expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwindow_expression_glosry.htm "Glossary Entry") work:
- Define a subset of the result set (i. e. the
"[window](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwindow_glosry.htm "Glossary Entry")")
of a database query that implements ABAP SQL
- Apply a [window function](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwindow_function_glosry.htm "Glossary Entry") -
which evaluates the rows of the window and which can, for example,
be an [aggregate
function](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenaggregate_function_glosry.htm "Glossary Entry")
like
[`AVG`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_agg_func&sap-language=EN&sap-client=000&version=X&anchor=!ABAP_VARIANT_1@1@&tree=X)
to determine the average value - to the result set
- I. e. a window is constructed by the rows of the result set for
which all the window functions have the same result; a value is then
determined for the rows of a window
Setup of a statement with window expressions:
- Window function, e. g. an aggregate function like `AVG`,
followed by `OVER( ... )` (the content in the parentheses
defines the "window")
- The content in the parentheses can contain the following additions:
- Optional `PARTITION BY`: Defines the windows using a
comma-separated list of [SQL
expressions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapsql_expr.htm);
the window function is calculated for the rows of this window;
note that if the addition is not specified, the window comprises
all rows of the result set
- Optional `ORDER BY`: Introduces both an order (you can
use `ASCENDING` and `DESCENDING`) and a frame
(as outlined below) within the current window, which further
restricts the rows for which the window function is calculated
- A window frame, which stands for a subset of rows inside a
window, can optionally be defined if `ORDER BY` is
specified; there are 3 options to define the starting and ending
frame boundaries (see the example)
See more information on window expressions and the syntax
[here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abapselect_over.htm).
Examples:
``` abap
"Example 1: A simple window is constructed in the OVER clause;
"window functions - here aggregate functions - are applied
SELECT carrid, currency,
SUM( paymentsum ) OVER( PARTITION BY carrid ) AS sum,
AVG( price AS DEC( 14,2 ) ) OVER( PARTITION BY carrid ) AS avg,
MAX( price ) OVER( PARTITION BY carrid ) AS max
FROM zdemo_abap_fli
ORDER BY carrid
INTO TABLE @DATA(win).
"Example 2:
SELECT carrid, currency, fldate,
"Sorts the rows by some columns and counts the number of rows from
"the first row of the window to the current row.
COUNT( * ) OVER( ORDER BY currency, fldate
ROWS BETWEEN
"UNBOUNDED PRECEDING: frame starts at the first row of the window
UNBOUNDED PRECEDING
"CURRENT ROW: determines starting or ending at the current row; here, it ends
AND CURRENT ROW ) AS count1,
"If no window frame is used, the default window frame is
"BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,
"i. e. the result of count1 equals the result of count2.
COUNT( * ) OVER( ORDER BY currency, fldate ) AS count2,
"Sorts the rows by some columns and counts the number of rows from
"the current row to the last row of the window.
"The result is reverse numbering.
COUNT( * ) OVER( ORDER BY currency, fldate
ROWS BETWEEN CURRENT ROW
UNBOUND FOLLOWING:
"Determines the ending frame boundary, this addition specifies the last row of the window
AND UNBOUNDED FOLLOWING ) AS count_reverse,
"Sorts the rows by some columns and calculates the rolling averages
"of a subset of rows from column price. The subset consists of the
"current row plus one preceding and one following row. Another use
"case as below example that uses prices would be that, for example,
"you can calculate the 3-day-average temperature for every day from
"a list of temperature data.
AVG( price AS DEC( 14,2 ) ) OVER( ORDER BY currency, fldate
ROWS BETWEEN
"n PRECEDING: for both start and end of frame; frame to start/end n rows above the current row
1 PRECEDING
"n FOLLOWING: for both start and end of frame; frame to start/end n rows beneath the current row
AND 1 FOLLOWING ) AS avg
FROM zdemo_abap_fli
INTO TABLE @DATA(result).
```
### Null Expressions
The [null value](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennull_value_glosry.htm) is represented by the `NULL` operand in ABAP SQL statements. Find more information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_null.htm).
```abap
SELECT
carrid,
carrname,
"The type of the null value is determined by the context.
"When the null value is passed to the internal table,
"it is converted to the initial value. In the first case,
"it is ' '. In the second case, it is 0..
CASE WHEN length( carrname ) > 12 THEN char`X`
ELSE NULL
END AS long_name,
CAST( NULL AS INT1 ) AS null_val
FROM zdemo_abap_carr
INTO TABLE @DATA(null_expr).
```
## Built-In SQL Functions
- Built-in SQL functions can be called in ABAP SQL.
- The result is a value with the associated dictionary type.
- The arguments of the functions can cover one or more SQL expressions.
- For all available functions and for more information, refer to [this topic](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_sql_builtin_functions.htm) in the ABAP Keyword Documentation and the subtopics.
- The functions are also covered in other cheat sheets such as the [Built-In Functions](24_Builtin_Functions.md) cheat sheet.
### Numeric Functions
[Numeric functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_arith_func.htm)
``` abap
SELECT SINGLE
carrname,
"Division, result rounded to an integer
"Result: 2
div( 4, 2 ) AS div,
"Division, 3rd argument: result is rounded to the specified
"number of decimals
"Result: 0.33
division( 1, 3, 2 ) AS division,
"Result is rounded to first greater integer
"Result: 2
ceil( decfloat34`1.333` ) AS ceil,
"Result is the remainder of division
"Result: 1
mod( 3, 2 ) AS mod,
"Result: Largest integer value not greater than the specified value
"Result: 1
floor( decfloat34`1.333` ) AS floor,
"Returns the absolute number
"Result: 2
abs( int4`-2` ) AS abs,
"Result is rounded to the specified position after the decimal separator
"Result: 1.34
round( decfloat34`1.337`, 2 ) AS round
FROM zdemo_abap_carr
WHERE carrid = 'AA'
INTO @DATA(numeric_functions).
```
### String Functions
[String functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_string_func.htm)
``` abap
SELECT SINGLE
carrid, "LH
carrname, "Lufthansa
url, "http://www.lufthansa.com
"Concatenates strings, ignores trailing blanks
"Result: LHLufthansa
concat( carrid, carrname ) AS concat,
"Concatenates strings, number denotes the blanks that are inserted
"Result: LH Lufthansa
concat_with_space( carrid, carrname, 1 ) AS concat_with_space,
"First letter of a word -> uppercase, all other letters -> lowercase;
"note that a space and other special characters means a new word.
"Result: Http://Www.Lufthansa.Com
initcap( url ) AS initcap,
"Position of the first occurrence of the substring specified
"Result: 6
instr( carrname,'a' ) AS instr,
"String of length n starting from the left of an expression;
"trailing blanks are ignored
"Result: Luft
left( carrname, 4 ) AS left,
"Number of characters in an expression, trailing blanks are ignored
"Result: 24
length( url ) AS length,
"Checks if expression contains a PCRE expression;
"case-sensitive by default (case_sensitive parameter can be specified)
"Notes on the result: 1 = found, 0 = not found
"Result: 1
like_regexpr( pcre = '\..', "Period that is followed by any character
value = url ) AS like_regex,
"Returns position of a substring in an expression,
"3rd parameter = specifies offset (optional)
"4th parameter = determines the number of occurrences (optional)
"Result: 9
locate( carrname, 'a', 0, 2 ) AS locate,
"Searches a PCRE pattern, returns offset of match;
"many optional parameters: occurrence, case_sensitive, start, group
"Result: 21
locate_regexpr( pcre = '\..', "Period followed by any character
value = url,
occurrence = 2 ) "2nd occurrence in the string
AS locate_regexpr,
"Searches a PCRE pattern, returns offset of match + 1;
"many optional parameters: occurrence, case_sensitive, start, group
"Result: 2
locate_regexpr_after( pcre = '.', "Any character
value = url,
occurrence = 1 ) AS locate_regexpr_after,
"Removes leading characters as specified in the 2nd argument,
"trailing blanks are removed
"Result: ufthansa
ltrim( carrname, 'L' ) AS ltrim,
"Counts all occurrences of found PCRE patterns
"Result: 2
occurrences_regexpr( pcre = '\..', "Period that is followed by any character
value = url ) AS occ_regex,
"Replaces the 2nd argument with the 3rd in an expression
"Result: Lufth#ns#
replace( carrname, 'a', '#' ) AS replace,
"Replaces a found PCRE expression;
"more parameters possible: occurrence, case_sensitive, start
"Result: http://www#ufthansa#om
replace_regexpr( pcre = '\..', "Period that is followed by any character
value = url,
with = '#' ) AS replace_regex,
"Extracts a string with the length specified starting from the right
"Result: hansa
right( carrname, 5 ) AS right,
"Expands string to length n (2nd argument); trailing blanks produced
"are replaced by the characters from the (3rd) argument
"Note that if n is less than the string, the expression is truncated
"on the right.
"Result: Lufthansa###
rpad( carrname, 12, '#' ) AS rpad,
"All trailing characters that match the character of the 2nd argument
"are removed; trailing blanks are removed, too
"Result: Lufthans
rtrim( carrname, 'a' ) AS rtrim,
"Returns a substring; 2nd argument = position from where to start;
"3rd argument: length of the extracted substring
"Result: fth
substring( carrname, 3, 3 ) AS substring,
"Searches for a PCRE expression and returns the matched substring
"More parameters possible: occurrence, case_sensitive, start, group
"Result: .lu
substring_regexpr( pcre = '\...', "Period that is followed by any two characters
value = url ) AS substring_regexpr,
"All lower case letters are transformed to upper case letters
"Result: LUFTHANSA
upper( carrname ) AS upper
FROM zdemo_abap_carr
WHERE carrid = 'LH'
INTO @DATA(string_functions).
```
### coalesce Function
[coalesce](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_coalesce.htm)
```abap
"The null value is a special value that is returned by a database. It indicates an
"undefined value or result. Note that, in ABAP, there are no special null values. Do
"not confuse the null value with a type-dependent initial value. When using SELECT
"statements to read data, null values can be produced by, for example, outer joins.
"When the null values are passed to a data object, they are transformed to the
"type-dependent initial values. For more information, refer to the ABAP Keyword Documentation.
"The following example uses a left outer join to intentionally create null values. For
"this purpose, two demo database tables of the ABAP cheat sheet repository are cleared and
"populated with specific values to visualize null values.
DELETE FROM zdemo_abap_tab1.
DELETE FROM zdemo_abap_tab2.
MODIFY zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 1 char1 = 'a' char2 = 'y' )
( key_field = 2 char1 = 'b' char2 = 'z' ) ) ).
MODIFY zdemo_abap_tab2 FROM TABLE @( VALUE #( ( key_field = 1 char1 = 'a' )
( key_field = 2 char1 = 'a' )
( key_field = 3 char1 = 'b' )
( key_field = 4 ) ) ).
"Note that for the entry 'key_field = 4' no char1 value was passed.
"char1 is a shared column of the two database tables, and which is used in
"the ON condition of the join. Since there is no entry in char1 for 'key_field = 4',
"the joined values are null in that case.
"The coalesce function is used to replace null values produced by an outer join with
"a different value.
SELECT tab2~key_field,
coalesce( tab1~char1, '-' ) AS coalesced1,
coalesce( tab1~char2, '#' ) AS coalesced2,
"A coalesce function is a short form of a complex
"case distinction such as the following:
CASE WHEN tab1~char1 IS NOT NULL THEN tab1~char1
ELSE '?'
END as coalesced3
FROM zdemo_abap_tab2 AS tab2
LEFT OUTER JOIN zdemo_abap_tab1 AS tab1 ON tab1~char1 = tab2~char1
INTO TABLE @DATA(join_w_null).
*Example table content
*KEY_FIELD COALESCED1 COALESCED2 COALESCED3
*1 a y a
*2 a y a
*3 b z b
*4 - # ?
```
### Conversion Functions
[Type conversion functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/ABENSQL_TYPE_CONV_FUNC.html):
| Function | Notes | Code Snippet |
| `bintohex( ... )` | Converts byte strings (type `raw`; mapped to ABAP type `x`) to character strings (type `char`) |
The code snippet implements the following:
- To have a self-contained example, a demo internal table with elementary line type (byte-like type `x length 10`) is created.
- The table is filled with demo data.
- An ABAP SQL `SELECT` statement that includes the `bintohex` function retrieves data from the internal table. Note that a warning would be displayed that the `SELECT` command is executed on the database. The warning is suppressed with a pragma.
- [Runtime Type Identification (RTTI)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenrun_time_type_identific_glosry.htm) (find more information in the Dynamic Programming cheat sheet) is used to demonstrate that the type of the `tline` (the alias name for `table_line`) is character-like.
``` abap TYPES x10 TYPE x LENGTH 10. TYPES ty_raw_tab TYPE TABLE OF x10 WITH EMPTY KEY. DATA(raw_tab) = VALUE ty_raw_tab( ( CONV x10( '68656C6C6F' ) ) ( CONV x10( '776F726C64' ) ) ( CONV x10( '41424150' ) ) ). SELECT bintohex( table_line ) AS tline FROM @raw_tab AS tab INTO TABLE @DATA(conv_to_blob_tab) ##ITAB_DB_SELECT. DATA(tdo_itab) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( conv_to_blob_tab ) ). DATA(table_components_itab) = CAST cl_abap_structdescr( tdo_itab->get_table_line_type( ) )->components. DATA(text_line_type_kind) = table_components_itab[ name = 'TLINE' ]-type_kind. ASSERT text_line_type_kind = cl_abap_typedescr=>typekind_char. ``` |
| `hextobin( ... )` | Converts character strings (type `char` or `numc`) to byte strings (type `raw`; mapped to ABAP type `x`) |
The code snippet implements the following:
- To have a self-contained example, a demo internal table with elementary line type (character-like type `c length 10`) is created.
- The table is filled with demo data.
- An ABAP SQL `SELECT` statement that includes the `hextobin` function retrieves data from the internal table. Note that a warning would be displayed that the `SELECT` command is executed on the database. The warning is suppressed with a pragma.
- RTTI is used to demonstrate that the type of the `tline` (the alias name for `table_line`) is `x`.
``` abap TYPES c10 TYPE c LENGTH 10. TYPES ty_c_tab TYPE TABLE OF c10 WITH EMPTY KEY. DATA(c_tab) = VALUE ty_c_tab( ( '68656C6C6F' ) ( '776F726C64' ) ( '41424150' ) ). SELECT hextobin( table_line ) AS tline FROM @c_tab AS tab INTO TABLE @DATA(hextobin_tab) ##ITAB_DB_SELECT. DATA(tdo_itab) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( hextobin_tab ) ). DATA(table_components_itab) = CAST cl_abap_structdescr( tdo_itab->get_table_line_type( ) )->components. DATA(text_line_type_kind) = table_components_itab[ name = 'TLINE' ]-type_kind. ASSERT text_line_type_kind = cl_abap_typedescr=>typekind_hex. ``` |
| `to_blob( ... )` | Converts from a byte field (type `raw`; mapped to ABAP type `x`) to a byte string (a blob, Binary Large Object; type `rawstring`; mapped to ABAP type `xstring`). |
The code snippet implements the following:
- To have a self-contained example, a demo internal table with elementary line type (byte-like type, `x length 10`) is created.
- The table is filled with demo data.
- An ABAP SQL `SELECT` statement that includes the `to_blob` function retrieves data from the internal table. Note that a warning would be displayed that the `SELECT` command is executed on the database. The warning is suppressed with a pragma.
- RTTI is used to demonstrate that the type of the `tline` (the alias name for `table_line`) is `xstring`.
``` abap TYPES x10 TYPE x LENGTH 10. TYPES ty_raw_tab TYPE TABLE OF x10 WITH EMPTY KEY. DATA(raw_tab) = VALUE ty_raw_tab( ( CONV x10( '68656C6C6F' ) ) ( CONV x10( '776F726C64' ) ) ( CONV x10( '41424150' ) ) ). SELECT to_blob( table_line ) AS tline FROM @raw_tab AS tab INTO TABLE @DATA(conv_to_blob_tab) ##ITAB_DB_SELECT. DATA(tdo_itab) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( conv_to_blob_tab ) ). DATA(table_components_itab) = CAST cl_abap_structdescr( tdo_itab->get_table_line_type( ) )->components. DATA(text_line_type_kind) = table_components_itab[ name = 'TLINE' ]-type_kind. ASSERT text_line_type_kind = cl_abap_typedescr=>typekind_xstring. ``` |
| `to_clob( ... )` | - Converts a fixed-length character string (type `char` or `sstring`) to a clob (character large object; type `string`) - The argument specified in the parentheses can be a table column, literal, host variable/constant, or an SQL expression |
The example class, executable with F9 in ADT, implements the following:
- To have a self-contained example, a demo internal table is created. Among others, it includes the `text` component that is of type `c` with length 255.
- The table is filled with lots of data. `text` receives random strings of a random length (1 - 255) to have data to work with.
- An ABAP SQL `SELECT` statement that includes the `to_clob` function retrieves data from the internal table. Note that a warning would be displayed that the `SELECT` command is executed on the database. The warning is suppressed with a pragma.
- The `to_clob` has an SQL expression as argument. In this case, it is an aggregate expression with `STRING_AGG`, which aggregates the value of multiple rows into a single value.
- Using RTTI, it is demonstrated that the type of the `text_line` component (the alias name for `text`) is `string`. It is then evaluated how many characters the random strings contain. The example is set up to show that the aggregated string exceeds 1333 characters, which is the maximum length of fields of type `sstring`.
```abap CLASS zcl_demo_abap DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_oo_adt_classrun. METHODS get_random_string IMPORTING VALUE(length) TYPE i OPTIONAL RETURNING VALUE(str) TYPE string. TYPES: BEGIN OF demo_struct, num TYPE i, uuid TYPE sysuuid_x16, text TYPE c LENGTH 255, END OF demo_struct. TYPES ty_demo_tab TYPE SORTED TABLE OF demo_struct WITH UNIQUE KEY num uuid. METHODS get_random_table_content IMPORTING VALUE(table_entry_count) TYPE i OPTIONAL RETURNING VALUE(demo_tab) TYPE ty_demo_tab. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_demo_abap IMPLEMENTATION. METHOD if_oo_adt_classrun~main. DATA(random_table) = get_random_table_content( 500 ). SELECT num, to_clob( STRING_AGG( text, ',' ) ) AS text_line FROM @random_table AS tab GROUP BY num ORDER BY num INTO TABLE @DATA(aggregated_data) ##ITAB_DB_SELECT. "Checking the type of the text_line component using RTTI DATA(tdo_itab) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( aggregated_data ) ). DATA(table_components_itab) = CAST cl_abap_structdescr( tdo_itab->get_table_line_type( ) )->components. DATA(text_line_type_kind) = table_components_itab[ name = 'TEXT_LINE' ]-type_kind. IF text_line_type_kind = cl_abap_typedescr=>typekind_string. out->write( `The text_line component is of type string.` ). out->write( |\n| ). ENDIF. DATA(lines_w_more_than_1333) = REDUCE i( INIT int = 0 FOR line IN aggregated_data NEXT int = COND #( WHEN strlen( line-text_line ) > 1333 THEN int + 1 ELSE int ) ). out->write( |{ lines_w_more_than_1333 } out of { lines( aggregated_data ) } lines of the internal table have a text_line value exceeding 1333 characters.| ). out->write( |\n| ). ENDMETHOD. METHOD get_random_string. IF length IS NOT SUPPLIED OR length > 255. length = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 1 max = 255 )->get_next( ). ENDIF. DATA(characters) = `aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ1234567890`. DATA(off) = strlen( characters ) - 1. DO length TIMES. DATA(random_offset) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 0 max = off )->get_next( ). TRY. str &&= characters+random_offset(1). CATCH cx_sy_range_out_of_bounds. ENDTRY. ENDDO. ENDMETHOD. METHOD get_random_table_content. IF table_entry_count IS NOT SUPPLIED OR table_entry_count > 1000. table_entry_count = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 1 max = 1000 )->get_next( ). ENDIF. "Example implementation: For the string aggregation in the SELECT statement, "multiple lines should be created with the same key value in 'num'. DATA(value4num) = 1. DO table_entry_count TIMES. IF sy-index MOD 25 = 0. value4num += 1. ENDIF. INSERT VALUE #( num = value4num uuid = xco_cp=>uuid( )->value text = get_random_string( ) ) INTO TABLE demo_tab. ENDDO. ENDMETHOD. ENDCLASS. ``` |
| `unit_conversion( ... )` | Converts units for a value passed to the `quantity` parameter |
The code snippet implements the following:
- To have a self-contained example, a demo internal table with elementary line type (packed number, `p length 16 decimals 14`) is created.
- The table is filled with demo data.
- Two ABAP SQL `SELECT` statements are included that specify the `unit_conversion` function. Data is retrieved from the internal table and converted. Note that a warning would be displayed that the `SELECT` command is executed on the database. The warning is suppressed with a pragma.
- The example covers the conversion of miles to kilometers and vice versa.
```abap TYPES p_len16dec14 TYPE p LENGTH 16 DECIMALS 14. TYPES ty_plen16dec14_tab TYPE TABLE OF p_len16dec14 WITH EMPTY KEY. DATA(p_tab) = VALUE ty_plen16dec14_tab( ( CONV p_len16dec14( '1' ) ) ( CONV p_len16dec14( '5.7' ) ) ( CONV p_len16dec14( '4.2' ) ) ( CONV p_len16dec14( '25.78' ) ) ). SELECT unit_conversion( quantity = table_line, source_unit = unit`MI`, target_unit = unit`KM` ) AS miles_to_km FROM @p_tab AS tab INTO TABLE @DATA(unit_conv_mi2km_tab) ##ITAB_DB_SELECT. SELECT unit_conversion( quantity = miles_to_km, source_unit = unit`KM`, target_unit = unit`MI` ) AS km_to_miles FROM @unit_conv_mi2km_tab AS tab INTO TABLE @DATA(unit_conv_km2mi_tab) ##ITAB_DB_SELECT. ``` |
| `currency_conversion( ... )` | - Converts currencies for a value passed to the `amount` parameter - Multiple optional parameters can be specified (excluding `client` in ABAP for Cloud Development) |
The code snippet implements the following:
- To have a self-contained example, a demo internal table with elementary line type (packed number, `p length 16 decimals 2`) is created.
- The table is filled with demo data.
- An ABAP SQL `SELECT` statement that includes the `currency_conversion` function retrieves data from the internal table and converts currency values from Euro to US dollars. Note that a warning would be displayed that the `SELECT` command is executed on the database. The warning is suppressed with a pragma.
```abap TYPES p_len16dec2 TYPE p LENGTH 16 DECIMALS 2. TYPES ty_plen16dec2_tab TYPE TABLE OF p_len16dec2 WITH EMPTY KEY. DATA(p_tab) = VALUE ty_plen16dec2_tab( ( CONV p_len16dec2( '1' ) ) ( CONV p_len16dec2( '5.7' ) ) ( CONV p_len16dec2( '4.2' ) ) ( CONV p_len16dec2( '25.78' ) ) ). SELECT currency_conversion( amount = table_line, source_currency = char`EUR`, target_currency = char`USD`, exchange_rate_date = @( cl_abap_context_info=>get_system_date( ) ) ) AS eur2usd FROM @p_tab AS tab INTO TABLE @DATA(curr_unit_conv_eur2usd) ##ITAB_DB_SELECT. ``` |
| `as_geo_json( ... )` | Converts geometry input in the [Extended Well-Known Binary (EWKB) representation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/ABENDDIC_GEO_DATA.html) to a geometry object in JSON format | ```abap SELECT as_geo_json( some_geo_field ) FROM ... WHERE ... INTO ... ``` |
| ABAP repository object | Notes/Code |
| Table entity | ```abap @ClientHandling.type: #CLIENT_INDEPENDENT @AbapCatalog.deliveryClass: #APPLICATION_DATA @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'CDS table entity' define table entity zdemo_abap_animals_te { key id : abap.int4; animal_name : abap.char(20); species : abap.char(20); age : abap.int4; country_of_origin : abap.char(20); arrival_date : abap.datn; is_carnivore : abap_boolean; } ``` |
| Writable CDS view entity | ```abap @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Writable CDS view entity' @Metadata.ignorePropagatedAnnotations: true define writable view entity zdemo_abap_animals_we as select from zdemo_abap_animals_te { key id, animal_name, species, age, country_of_origin, arrival_date, is_carnivore } ``` |
| Class | ```abap CLASS zcl_demo_abap DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_oo_adt_classrun. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_demo_abap IMPLEMENTATION. METHOD if_oo_adt_classrun~main. *&---------------------------------------------------------------------* *& Using table entities and writable CDS view entities as data types *& in ABAP *&---------------------------------------------------------------------* DATA struc_table_entity TYPE zdemo_abap_animals_te. TYPES struc_type_table_entity TYPE zdemo_abap_animals_te. DATA(another_struc_table_ent) = VALUE zdemo_abap_animals_te( id = 1 animal_name = 'Zeus' ). DATA struc_writable_ve TYPE zdemo_abap_animals_we. TYPES struc_type_writable_ve TYPE zdemo_abap_animals_we. DATA(another_struc_writable_ve) = VALUE zdemo_abap_animals_we( id = 2 animal_name = 'Leo' ). *&---------------------------------------------------------------------* *& Performing CRUD operations using a table entity and ABAP SQL *&---------------------------------------------------------------------* out->write( `---- Performing CRUD operations on table entities using ABAP SQL ----` ). out->write( |\n| ). DELETE FROM zdemo_abap_animals_te. "DATA animals_table type table of zdemo_abap_animals_te with empty key. INSERT zdemo_abap_animals_te FROM TABLE @( VALUE #( ( id = 1 animal_name = 'Zeus' species = 'Lion' age = 5 country_of_origin = '' arrival_date = '20220115' is_carnivore = abap_true ) ( id = 2 animal_name = 'Leo' species = 'Lion' age = 7 country_of_origin = '' arrival_date = '20241205' is_carnivore = abap_true ) ( id = 3 animal_name = 'Jumbo' species = 'Elephant' age = 10 country_of_origin = 'India' arrival_date = '20190526' is_carnivore = abap_false ) ( id = 4 animal_name = 'Little' species = 'Elephant' age = 4 country_of_origin = 'India' arrival_date = '20240314' is_carnivore = abap_false ) ( id = 5 animal_name = 'Daisy' species = 'Zebra' age = 4 country_of_origin = 'South Africa' arrival_date = '20220207' is_carnivore = abap_false ) ( id = 6 animal_name = 'Buddy' species = 'Owl' age = 3 country_of_origin = 'Canada' arrival_date = '20210111' is_carnivore = abap_true ) ( id = 7 animal_name = 'Sunny' species = 'Dolphin' age = 7 country_of_origin = 'Australia' arrival_date = '20190527' is_carnivore = abap_false ) ( id = 8 animal_name = 'Nala' species = 'Giraffe' age = 6 country_of_origin = 'Tanzania' arrival_date = '20210807' is_carnivore = abap_false ) ( id = 9 animal_name = 'Oscar' species = 'Tiger' age = 8 country_of_origin = 'Russia' arrival_date = '20191018' is_carnivore = abap_true ) ( id = 10 animal_name = 'Charlie' species = 'Chimpanzee' is_carnivore = abap_false ) ( id = 12 ) ) ). out->write( |sy-dbcnt after 1st INSERT: { sy-dbcnt }| ). SELECT COUNT(*) FROM zdemo_abap_animals_te INTO @DATA(number_of_entries). out->write( |Number of data entries after 1st INSERT: { number_of_entries }| ). INSERT zdemo_abap_animals_te FROM TABLE @( VALUE #( ( id = 1 animal_name = 'Lora' species = 'Parrot' age = 2 country_of_origin = 'Mexico' arrival_date = '20240712' is_carnivore = abap_false ) ( id = 11 animal_name = 'Teddy' species = 'Koala' age = 4 country_of_origin = 'Australia' arrival_date = '20240225' is_carnivore = abap_false ) ) ) ACCEPTING DUPLICATE KEYS. out->write( |sy-dbcnt after 2nd INSERT: { sy-dbcnt }| ). SELECT COUNT(*) FROM zdemo_abap_animals_te INTO @number_of_entries. out->write( |Number of data entries after 1st INSERT: { number_of_entries }| ). MODIFY zdemo_abap_animals_te FROM TABLE @( VALUE #( ( id = 12 animal_name = 'Peanut' species = 'Penguin' age = 6 country_of_origin = 'Antarctica' arrival_date = '20200410' is_carnivore = abap_false ) ( id = 13 animal_name = 'Lora' species = 'Parrot' age = 2 country_of_origin = 'Mexico' arrival_date = '20240712' is_carnivore = abap_false ) ( id = 14 animal_name = 'Jumpy' species = 'Kangaroo' age = 4 country_of_origin = 'Australia' arrival_date = '20211117' is_carnivore = abap_false ) ) ). out->write( |sy-dbcnt after MODIFY: { sy-dbcnt }| ). SELECT COUNT(*) FROM zdemo_abap_animals_te INTO @number_of_entries. out->write( |Number of data entries after MODIFY: { number_of_entries }| ). SELECT SINGLE * FROM zdemo_abap_animals_te WHERE id = 10 INTO @DATA(data_set). UPDATE zdemo_abap_animals_te FROM @( VALUE #( BASE data_set age = 3 country_of_origin = 'Uganda' arrival_date = '20231214' ) ). out->write( |sy-dbcnt after 1st UPDATE: { sy-dbcnt }| ). UPDATE zdemo_abap_animals_te SET country_of_origin = 'Kenya' WHERE country_of_origin IS INITIAL. out->write( |sy-dbcnt after 2nd UPDATE: { sy-dbcnt }| ). SELECT * FROM zdemo_abap_animals_te ORDER BY id INTO TABLE @DATA(itab_te). out->write( `Table entries retrieved from table entity:` ). out->write( itab_te ). DELETE FROM zdemo_abap_animals_te WHERE id > 10. out->write( |sy-dbcnt after DELETE: { sy-dbcnt }| ). SELECT COUNT(*) FROM zdemo_abap_animals_te INTO @number_of_entries. out->write( |Number of data entries after DELETE: { number_of_entries }| ). out->write( |\n| ). out->write( repeat( val = '_' occ = 100 ) ). out->write( |\n| ). *&---------------------------------------------------------------------* *& Performing CRUD operations using a writable CDS view entity and ABAP SQL *&---------------------------------------------------------------------* out->write( `---- Performing CRUD operations on a writable CDS view entity using ABAP SQL ----` ). out->write( |\n| ). SELECT COUNT(*) FROM zdemo_abap_animals_we INTO @DATA(we_number_of_entries). out->write( |Number of data entries accessed via writable CDS view entity: { we_number_of_entries }| ). INSERT zdemo_abap_animals_we FROM TABLE @( VALUE #( ( id = 15 ) ( id = 16 animal_name = 'Fuzz' species = 'Polar bear' ) ) ). out->write( |sy-dbcnt after INSERT: { sy-dbcnt }| ). SELECT COUNT(*) FROM zdemo_abap_animals_we INTO @we_number_of_entries. out->write( |Number of data entries after INSERT: { we_number_of_entries }| ). MODIFY zdemo_abap_animals_we FROM @( VALUE #( id = 15 animal_name = 'Hunter' species = 'Jaguar' age = 3 country_of_origin = 'Brazil' arrival_date = '20230423' is_carnivore = abap_true ) ). out->write( |sy-dbcnt after MODIFY: { sy-dbcnt }| ). SELECT SINGLE * FROM zdemo_abap_animals_we WHERE id = 16 INTO @DATA(entry). UPDATE zdemo_abap_animals_we FROM @( VALUE #( BASE entry animal_name = 'Fuzzy' age = 7 country_of_origin = 'Canada' arrival_date = '20190223' is_carnivore = abap_true ) ). out->write( |sy-dbcnt after UPDATE: { sy-dbcnt }| ). DELETE zdemo_abap_animals_we FROM @( VALUE #( id = 10 ) ). out->write( |sy-dbcnt after 1st DELETE: { sy-dbcnt }| ). SELECT COUNT(*) FROM zdemo_abap_animals_we INTO @we_number_of_entries. out->write( |Number of data entries after 1st DELETE: { we_number_of_entries }| ). DELETE FROM zdemo_abap_animals_we WHERE id <= 5. out->write( |sy-dbcnt after 2nd DELETE: { sy-dbcnt }| ). SELECT COUNT(*) FROM zdemo_abap_animals_we INTO @we_number_of_entries. out->write( |Number of data entries after 2nd DELETE: { we_number_of_entries }| ). SELECT * FROM zdemo_abap_animals_we ORDER BY id INTO TABLE @DATA(itab_we). out->write( `Table entries retrieved via writable CDS view entity:` ). out->write( itab_we ). ENDMETHOD. ENDCLASS. ``` |