This commit is contained in:
danrega
2024-07-24 15:59:56 +02:00
parent 62ba5f78ac
commit 6cb92a5fcc
3 changed files with 168 additions and 87 deletions

View File

@@ -35,6 +35,7 @@
- [Getting Table (Type) Information at Runtime](#getting-table-type-information-at-runtime)
- [Processing Multiple Internal Table Lines Sequentially](#processing-multiple-internal-table-lines-sequentially)
- [Restricting the Area of a Table to Be Looped Over](#restricting-the-area-of-a-table-to-be-looped-over)
- [Defining the Step Size and the Direction of Loop Passes](#defining-the-step-size-and-the-direction-of-loop-passes)
- [Iteration Expressions](#iteration-expressions)
- [Interrupting and Exiting Loops](#interrupting-and-exiting-loops)
- [Operations with Internal Tables Using ABAP SQL SELECT Statements](#operations-with-internal-tables-using-abap-sql-select-statements)
@@ -797,7 +798,7 @@ INSERT VALUE s( a = 'yyy' b = 3 ) INTO TABLE dref_tab->*.
### Example: Exploring Populating Internal Tables
Expand the following collapsible section to view the code of an example. To try it out, create a demo class named `zcl_some_class` and paste the code into it. After activation, choose *F9* in ADT to execute the class.
The example is set up to display output in the console, but only for few data objects. You may want to set a break point at the first position possible and walk through the example in the debugger. You may want to set a breakpoint at the earliest possible position and walk through the example in the debugger. This will allow you to double-click on data objects and observe how the different statements affect their contents.
The example is set up to display output in the console, but only for few data objects. You may want to set a break point at the earliest possible position and walk through the example in the debugger. This will allow you to double-click on data objects and observe how the different statements affect their contents.
<details>
<summary>Expand to view the code</summary>
@@ -1517,10 +1518,17 @@ READ TABLE itab WITH KEY ... BINARY SEARCH ...
- Depending on the number of times you need to access the internal table, it is recommended to work with sorted tables or tables with secondary keys. If you only need to read one or a few data sets, consider the administrative costs of setting up the index.
- Note: The `BINARY SEARCH` addition is not available for table expressions. If `KEY ...` is specified, an optimized search is performed by default. There are no performance differences between using the `READ TABLE` statement and table expressions.
The output of the following example, which includes multiple reads on standard internal tables using `READ TABLE` statements without `BINARY SEARCH` and with `BINARY SEARCH` demonstrates the performance gain. An excursion is included that shows read accesses in an internal table with a secondary table key.
To try it out the following example, create a demo class named `zcl_some_class` and paste the code into it. After activation, choose *F9* in ADT to execute the class. The example is set up to display output in the console.
The example includes multiple reads on standard internal tables using a `READ TABLE` statement ...
- without `BINARY SEARCH`.
- with `BINARY SEARCH` and a previous `SORT` statement.
- with a secondary table key whose components correspond to free key used in the previous statements.
The runtime of the reads is determined and stored in internal tables. The read operations are repeated several times to have a more accurate runtime evaluation. The fastest time is output.
The example is intended to demonstrate the performance gain using the `BINARY SEARCH` addition (and also using a secondary table key).
```abap
CLASS zcl_demo_test DEFINITION
CLASS zcl_some_class DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
@@ -1533,7 +1541,7 @@ ENDCLASS.
CLASS zcl_demo_test IMPLEMENTATION.
CLASS zcl_some_class IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
"Line type and internal table declarations
TYPES: BEGIN OF demo_struc,
@@ -1542,16 +1550,19 @@ CLASS zcl_demo_test IMPLEMENTATION.
num TYPE i,
END OF demo_struc.
DATA: "Tables with empty primary table key
itab_std1 TYPE STANDARD TABLE OF demo_struc WITH EMPTY KEY,
itab_std2 LIKE itab_std1,
"Table with empty primary table key, secondary table key specified
itab_sec TYPE STANDARD TABLE OF demo_struc
WITH EMPTY KEY
WITH NON-UNIQUE SORTED KEY sk COMPONENTS str num.
"Tables with empty primary table key
DATA itab_std1 TYPE STANDARD TABLE OF demo_struc WITH EMPTY KEY.
DATA itab_std2 LIKE itab_std1.
"Table with empty primary table key, secondary table key specified
DATA itab_sec TYPE STANDARD TABLE OF demo_struc
WITH EMPTY KEY
WITH NON-UNIQUE SORTED KEY sk COMPONENTS str num.
DATA num_of_table_lines TYPE i VALUE 5000.
DATA num_of_repetitions TYPE i VALUE 10.
DATA num_of_reads TYPE i VALUE 3000.
"Populating internal tables
DO 1000 TIMES.
DO num_of_table_lines TIMES.
INSERT VALUE #( idx = sy-index
str = |INDEX{ sy-index }|
num = sy-index ) INTO TABLE itab_std1.
@@ -1559,57 +1570,68 @@ CLASS zcl_demo_test IMPLEMENTATION.
itab_std2 = itab_std1.
itab_sec = itab_std1.
"---- Reading without the BINARY SEARCH addition ----
DATA(ts1) = utclong_current( ).
DO 1000 TIMES.
READ TABLE itab_std1
WITH KEY str = `INDEX500` num = 500
REFERENCE INTO DATA(dref1).
DATA no_binary_search TYPE TABLE OF decfloat34 WITH EMPTY KEY.
DATA with_sort_and_binary_search TYPE TABLE OF decfloat34 WITH EMPTY KEY.
DATA with_secondary_key TYPE TABLE OF decfloat34 WITH EMPTY KEY.
"Repeating the reads several times for a more accurate result
DO num_of_repetitions TIMES.
"---- Reading without the BINARY SEARCH addition ----
DATA(ts1) = utclong_current( ).
DO num_of_reads TIMES.
READ TABLE itab_std1 WITH KEY str = `INDEX` && sy-index num = sy-index TRANSPORTING NO FIELDS.
ENDDO.
DATA(ts2) = utclong_current( ).
cl_abap_utclong=>diff( EXPORTING high = ts2
low = ts1
IMPORTING seconds = DATA(seconds) ).
APPEND seconds TO no_binary_search.
"---- Reading with the BINARY SEARCH addition ----
ts1 = utclong_current( ).
"Sorting the internal table when using BINARY SEARCH
"In this simple example, the internal table is populated by having the free key components
"to be searched in ascending order anyway. This is to emphasize the requirement to
"sort the (standard) internal table when using BINARY SEARCH. Here, the SORT statement
"is counted to the runtime.
SORT itab_std2 BY str num.
DO num_of_reads TIMES.
READ TABLE itab_std2 WITH KEY str = `INDEX` && sy-index num = sy-index BINARY SEARCH TRANSPORTING NO FIELDS.
ENDDO.
ts2 = utclong_current( ).
cl_abap_utclong=>diff( EXPORTING high = ts2
low = ts1
IMPORTING seconds = seconds ).
APPEND seconds TO with_sort_and_binary_search.
"---- Excursion: Reading with READ TABLE using a secondary table key ----
ts1 = utclong_current( ).
DO num_of_reads TIMES.
READ TABLE itab_sec WITH TABLE KEY sk COMPONENTS str = `INDEX` && sy-index num = sy-index TRANSPORTING NO FIELDS.
ENDDO.
ts2 = utclong_current( ).
cl_abap_utclong=>diff( EXPORTING high = ts2
low = ts1
IMPORTING seconds = seconds ).
APPEND seconds TO with_secondary_key.
ENDDO.
DATA(ts2) = utclong_current( ).
cl_abap_utclong=>diff( EXPORTING high = ts2
low = ts1
IMPORTING seconds = DATA(seconds) ).
out->write( `Elapsed time for the reads using READ TABLE without the BINARY SEARCH addition:` ).
out->write( seconds ).
out->write( repeat( val = `-` occ = 70 ) ).
SORT no_binary_search ASCENDING BY table_line.
SORT with_sort_and_binary_search ASCENDING BY table_line.
SORT with_secondary_key ASCENDING BY table_line.
"---- Reading with the BINARY SEARCH addition ----
ts1 = utclong_current( ).
"Sorting the internal table when using BINARY SEARCH
"In this simple example, the internal table is populated by having the free key components
"to be searched in ascending order anyway. This is to emphasize the requirement to
"sort the (standard) internal table when using BINARY SEARCH.
SORT itab_std2 BY str num.
DO 1000 TIMES.
READ TABLE itab_std2
WITH KEY str = `INDEX500` num = 500
BINARY SEARCH
REFERENCE INTO DATA(dref2).
ENDDO.
ts2 = utclong_current( ).
cl_abap_utclong=>diff( EXPORTING high = ts2
low = ts1
IMPORTING seconds = seconds ).
out->write( `Elapsed time for the reads using READ TABLE ... BINARY SEARCH ...:` ).
out->write( seconds ).
out->write( repeat( val = `-` occ = 70 ) ).
"---- Excursion: Reading with READ TABLE using a secondary table key ----
ts1 = utclong_current( ).
DO 1000 TIMES.
READ TABLE itab_sec
WITH TABLE KEY sk COMPONENTS str = `INDEX500` num = 500
INTO DATA(dref3).
ENDDO.
ts2 = utclong_current( ).
cl_abap_utclong=>diff( EXPORTING high = ts2
low = ts1
IMPORTING seconds = seconds ).
out->write( `Elapsed time for the reads using READ TABLE and a secondary table key:` ).
out->write( seconds ).
out->write( |Number of read repetitions: { num_of_repetitions }| ).
out->write( |Number of reads per table: { num_of_reads }\n| ).
out->write( `Fastest run of reads using READ TABLE with a free key, without the BINARY SEARCH addition:` ).
out->write( no_binary_search[ 1 ] ).
out->write( repeat( val = `-` occ = 70 ) ).
out->write( `Fastest run of reads using SORT, and READ TABLE with a free key, and the BINARY SEARCH addition:` ).
out->write( with_sort_and_binary_search[ 1 ] ).
out->write( repeat( val = `-` occ = 70 ) ).
out->write( `Fastest run of reads using READ TABLE and a secondary table key:` ).
out->write( with_secondary_key[ 1 ] ).
ENDMETHOD.
ENDCLASS.
```
@@ -2253,6 +2275,8 @@ line. This is not true for hashed tables. There, `sy-tabix` is `0`.
- Note that if you want to work with the value of `sy-tabix`, you
should do so immediately after the `LOOP` statement to avoid possible overwriting in statements contained in the loop block.
<p align="right"><a href="#top">⬆️ back to top</a></p>
### Restricting the Area of a Table to Be Looped Over
The additions of `LOOP` statements come into play when you want to restrict the table content to be respected by the loop because
@@ -2300,7 +2324,15 @@ LOOP AT it INTO wa USING KEY primary_key.
"LOOP AT it INTO wa USING KEY sk. "secondary key alias
...
ENDLOOP.
```
<p align="right"><a href="#top">⬆️ back to top</a></p>
### Defining the Step Size and the Direction of Loop Passes
Find more information in the [`STEP`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abaploop_at_itab_cond.htm#!ABAP_ADDITION_3@3@) topic. The addition is also available for other ABAP statements.
``` abap
"STEP addition for defining the step size and the direction of the loop
"- Step size: Specified by the absolute value of an integer
"- Direction: Specified by a positive (forward loop) or negative
@@ -2328,10 +2360,27 @@ LOOP AT it INTO wa FROM 6 TO 3 STEP -2.
ENDLOOP.
```
<p align="right"><a href="#top">⬆️ back to top</a></p>
### Iteration Expressions
Iteration expressions with [`FOR`](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfor.htm) as part of certain constructor expressions allow you to create content of an internal table by evaluating one or more source tables.
```abap
TYPES ty_int_tab TYPE TABLE OF i WITH EMPTY KEY.
DATA(int_table_a) = VALUE ty_int_tab( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ).
DATA int_table_b TYPE ty_int_tab.
int_table_b = VALUE #( FOR wa_b IN int_table_a ( wa_b * 2 ) ).
"Table Content: 2 / 4 / 6 / 8 / 10
"Instead of, for example, a LOOP statement as follows:
DATA int_table_c TYPE ty_int_tab.
LOOP AT int_table_a INTO DATA(wa_c).
INSERT wa_c * 3 INTO TABLE int_table_c.
ENDLOOP.
"Table content: 3 / 6 / 9 / 12 / 15
```
The expressions are covered in the cheat sheet [Constructor Expressions](05_Constructor_Expressions.md):
- [Iteration Expressions Using FOR](05_Constructor_Expressions.md#iteration-expressions-using-for)
- Special reduction operator `REDUCE` that is based on iteration expressions: [REDUCE](05_Constructor_Expressions.md#reduce)
@@ -3322,7 +3371,7 @@ CLASS zcl_some_class IMPLEMENTATION.
"Populating internal tables with demo data
DO number_of_lines TIMES.
INSERT VALUE #( idx = sy-index txt = |INDEX{ sy-index }| num = |{ sy-index }| ) INTO TABLE it_std_empty_key.
INSERT VALUE #( idx = sy-index txt = |INDEX{ sy-index }| num = sy-index ) INTO TABLE it_std_empty_key.
ENDDO.
"Copying the content to the other internal tables having the same line type
@@ -3392,8 +3441,10 @@ CLASS zcl_some_class IMPLEMENTATION.
"with all kinds of tables in the read accesses
IF dref->table_kind = 'H'.
ASSIGN dref->itab_ref->* TO <itab_ha>.
ASSIGN dref->itab_ref->* TO <any_tab>.
ELSE.
ASSIGN dref->itab_ref->* TO <itab_idx>.
ASSIGN dref->itab_ref->* TO <any_tab>.
ENDIF.
"-------------------- Read access by primary table index --------------------
@@ -3430,13 +3481,6 @@ CLASS zcl_some_class IMPLEMENTATION.
"statement below
DATA(sec_key_name) = dref->keys[ is_primary = '' ]-name.
"Assigning index or hashed table to a field symbol
IF <itab_idx> IS ASSIGNED.
ASSIGN <itab_idx> TO <any_tab>.
ELSEIF <itab_ha> IS ASSIGNED.
ASSIGN <itab_ha> TO <any_tab>.
ENDIF.
ts1 = utclong_current( ).
DO number_of_reads TIMES.
READ TABLE <any_tab> INDEX sy-index USING KEY (sec_key_name) TRANSPORTING NO FIELDS.
@@ -3467,11 +3511,6 @@ CLASS zcl_some_class IMPLEMENTATION.
ENDIF.
IF is_pr_key_only_idx = abap_true.
IF <itab_idx> IS ASSIGNED.
ASSIGN <itab_idx> TO <any_tab>.
ELSEIF <itab_ha> IS ASSIGNED.
ASSIGN <itab_ha> TO <any_tab>.
ENDIF.
dref->operation = |Read access by primary table key|.
ts1 = utclong_current( ).
@@ -3506,11 +3545,6 @@ CLASS zcl_some_class IMPLEMENTATION.
ENDIF.
IF is_sec_key_only_num = abap_true.
IF <itab_idx> IS ASSIGNED.
ASSIGN <itab_idx> TO <any_tab>.
ELSEIF <itab_ha> IS ASSIGNED.
ASSIGN <itab_ha> TO <any_tab>.
ENDIF.
dref->operation = |Read access by secondary table key|.
ts1 = utclong_current( ).
@@ -3535,11 +3569,6 @@ CLASS zcl_some_class IMPLEMENTATION.
"Checking whether the internal table includes the two components
"that constitute the free key.
IF check_example_components( ).
IF <itab_idx> IS ASSIGNED.
ASSIGN <itab_idx> TO <any_tab>.
ELSEIF <itab_ha> IS ASSIGNED.
ASSIGN <itab_ha> TO <any_tab>.
ENDIF.
dref->operation = |Read access by free key|.
ts1 = utclong_current( ).

View File

@@ -20,6 +20,7 @@
- [Aggregate Expressions](#aggregate-expressions)
- [Arithmetic, Cast, String Expressions, and Case Distinctions](#arithmetic-cast-string-expressions-and-case-distinctions)
- [Window Expressions](#window-expressions)
- [coalesce Function](#coalesce-function)
- [SQL Conditions](#sql-conditions)
- [Selecting Data by Evaluating the Content of Other Tables](#selecting-data-by-evaluating-the-content-of-other-tables)
- [Combining Data of Multiple Database Tables](#combining-data-of-multiple-database-tables)
@@ -626,7 +627,7 @@ SELECT FROM zdemo_abap_flsch
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.
- Built-in functions are available 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. Find more information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_sql_builtin_functions.htm).
- Built-in SQL functions can be specified as standalone functions in ABAP SQL or as operands of SQL expressions. The result is a value with the associated dictionary type. The arguments of the functions can cover one or more SQL expressions. Find more information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenabap_sql_builtin_functions.htm).
##### Elementary Expressions
@@ -1078,6 +1079,60 @@ SELECT carrid, currency, fldate,
<p align="right"><a href="#top">⬆️ back to top</a></p>
##### coalesce Function
Find more information [here](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 - # ?
```
<p align="right"><a href="#top">⬆️ back to top</a></p>
### SQL Conditions
You can formulate conditions in ABAP SQL statements, i. e. [logical

View File

@@ -1698,9 +1698,6 @@ DATA(url_parts_for_loop) = VALUE string_table( FOR wa1 IN res
*abap-concepts
*controlled-sap-luw
"More additions can be specified such as WHERE, USING KEY, FROM/TO, STEP
"WHERE condition