Files
abap-cheat-sheets/src/zcl_demo_abap_sql.clas.abap
danrega 270edb9962 Update
2024-12-20 16:12:45 +01:00

2194 lines
84 KiB
ABAP

"! <p class="shorttext"><strong>ABAP SQL</strong><br/>ABAP cheat sheet example class</p>
"!
"! <p>The example class demonstrates syntax and concepts related to ABAP SQL.<br/>
"! Choose F9 in ADT to run the class.</p>
"!
"! <h2>Note</h2>
"! <p>Find information on <strong>getting started with the example class</strong> and the
"! <strong>disclaimer</strong> in the ABAP Doc comment of class {@link zcl_demo_abap_aux}.</p>
CLASS zcl_demo_abap_sql DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES: if_oo_adt_classrun.
CLASS-METHODS:
class_constructor.
PROTECTED SECTION.
PRIVATE SECTION.
CLASS-METHODS: select_from_dbtab.
CLASS-DATA:
struct TYPE zdemo_abap_flsch,
itab TYPE TABLE OF zdemo_abap_flsch,
itab_res TYPE TABLE OF zdemo_abap_carr.
ENDCLASS.
CLASS zcl_demo_abap_sql IMPLEMENTATION.
METHOD class_constructor.
"Filling demo database tables.
zcl_demo_abap_aux=>fill_dbtabs( ).
ENDMETHOD.
METHOD if_oo_adt_classrun~main.
out->write( |ABAP cheat sheet example: ABAP SQL\n\n| ).
out->write( |Using SELECT for multiple purposes\n| ).
out->write( |1) Reading a single row from database table into a structure\n\n| ).
"Note that, although it is optional, a WHERE clause should always be
"specified for performance reasons and to restrict the read result.
"In the following SELECT statements, a simple WHERE condition is
"used to limit the number of found results.
"Reading all fields
"Reading into existing structure
SELECT SINGLE FROM zdemo_abap_flsch
FIELDS *
WHERE carrid = 'LH' AND connid = '400'
INTO @struct.
"Alternative syntax (no FIELDS), target variable declared inline
SELECT SINGLE *
FROM zdemo_abap_flsch
WHERE carrid = 'AA' AND connid = '17'
INTO @DATA(struct_1a).
out->write( data = struct name = `struct` ).
out->write( |\n| ).
out->write( data = struct_1a name = `struct_1a` ).
out->write( |\n| ).
"Reading selected fields
SELECT SINGLE carrid, connid, cityfrom, cityto
FROM zdemo_abap_flsch
WHERE carrid = 'AZ' AND connid = '555'
INTO @DATA(struct_1b).
"Alternative syntax (with FIELDS)
SELECT SINGLE
FROM zdemo_abap_flsch
FIELDS carrid, connid, cityfrom, cityto
WHERE carrid = 'DL' AND connid = '106'
INTO @DATA(struct_1c).
"When reading a selected set of fields into an existing target
"variable, the CORRESPONDING FIELDS OF addition in the INTO clause
"should be used. Other, not selected fields remain initial.
DATA struct_1d LIKE struct.
SELECT SINGLE carrid, connid, cityfrom, cityto
FROM zdemo_abap_flsch
WHERE carrid = 'DL' AND connid = '106'
INTO CORRESPONDING FIELDS OF @struct_1d.
out->write( data = struct_1b name = `struct_1b` ).
out->write( |\n| ).
out->write( data = struct_1c name = `struct_1c` ).
out->write( |\n| ).
out->write( data = struct_1d name = `struct_1d` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `2) Reading mutliple rows into an internal table` ) ).
"Reading all fields into an existing internal table
SELECT FROM zdemo_abap_flsch
FIELDS *
WHERE carrid = 'DL'
INTO TABLE @itab.
"Alternative syntax (no FIELDS), reading of a selected set of
"fields, internal table is declared inline
SELECT carrid, connid, cityfrom, cityto
FROM zdemo_abap_flsch
WHERE carrid = 'AZ'
INTO TABLE @DATA(itab_2a).
"When reading a selected set of fields into an existing target
"variable, the CORRESPONDING FIELDS OF addition in the INTO clause
"should be used. Other, not selected fields remain initial.
DATA itab_2b LIKE itab.
SELECT carrid, connid, cityfrom, cityto
FROM zdemo_abap_flsch
WHERE carrid = 'AZ'
INTO CORRESPONDING FIELDS OF TABLE @itab_2b.
out->write( data = itab name = `itab` ).
out->write( |\n| ).
out->write( data = itab_2a name = `itab_2a` ).
out->write( |\n| ).
out->write( data = itab_2b name = `itab_2b` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `3) SELECT loop: Sequentially reading multiple rows` ) ).
"In the example below, the individual rows that are read are
"modified before they are appended to an internal table.
DATA itab3 LIKE itab.
SELECT FROM zdemo_abap_flsch
FIELDS * "All fields
WHERE carrid = 'DL'
INTO @DATA(struct3).
"Further processing of the structure if the reading is successful
IF sy-subrc = 0.
"Modification: Converting miles to kilometers
IF struct3-distid = 'MI'.
struct3-distance = struct3-distance * '1.609344'.
struct3-distid = 'KM'.
ENDIF.
"Appending structure to an internal table
APPEND struct3 TO itab3.
ENDIF.
ENDSELECT.
out->write( data = itab3 name = `itab3` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `4) INTO CORRESPONDING FIELDS OF: Reading into existing` &&
` target variables that have a line type not matching the type of the data source` ) ).
"Note: The addition CORRESPONDING FIELDS OF is needed when using
"an existing variable to read data into, otherwise a type
"compatibility issue might arise because the SELECT statement fills
"the variable from left to right beginning with the first
"component. In the example below, the identically named fields have
"a matching type.
"Creating structure type, structure and internal table.
TYPES: BEGIN OF struc_type,
carrid TYPE zdemo_abap_flsch-carrid,
connid TYPE zdemo_abap_flsch-connid,
cityfrom TYPE zdemo_abap_flsch-cityfrom,
cityto TYPE zdemo_abap_flsch-cityto,
END OF struc_type.
DATA struc4 TYPE struc_type.
DATA itab4 TYPE TABLE OF struc_type.
"Reading into a structure that has not a matching type
SELECT SINGLE FROM zdemo_abap_flsch
FIELDS carrid, connid, cityfrom, cityto
WHERE carrid = 'AZ' AND connid = '555'
INTO CORRESPONDING FIELDS OF @struc4.
"Reading into an internal table that has not a matching type
SELECT FROM zdemo_abap_flsch
FIELDS *
WHERE carrid = 'AZ'
INTO CORRESPONDING FIELDS OF TABLE @itab4.
out->write( data = struc4 name = `struc4` ).
out->write( |\n| ).
out->write( data = itab4 name = `itab4` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Clause variations and additions in SELECT statements` ) ).
"SELECT/FROM clause variants
out->write( |SELECT/FROM clause variants\n| ).
out->write( |5) Checking the existence of a row in a database table\n| ).
"Instead of @abap_true, you could also use 'X' in the example below.
SELECT SINGLE @abap_true
FROM zdemo_abap_flsch
WHERE carrid = 'AZ' AND connid = '555'
INTO @DATA(exists).
IF exists = abap_true.
out->write( `A line was found.` ).
ELSE.
out->write( `Nothing found.` ).
ENDIF.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `6) DISTINCT addition: Removing duplicative rows from the result set` ) ).
"The example shows the comparison of statements with and without
"the use of DISTINCT. When used without DISTINCT, the result
"shows multiple entries whereas the statement with DISTINCT
"filters the duplicates out.
"DISTINCT addition
SELECT DISTINCT cityfrom
FROM zdemo_abap_flsch
WHERE carrid = 'LH' AND
cityto = 'NEW YORK'
INTO TABLE @DATA(itab_6a).
"Similar statement not using DISTINCT
SELECT cityfrom
FROM zdemo_abap_flsch
WHERE carrid = 'LH' AND
cityto = 'NEW YORK'
INTO TABLE @DATA(itab_6b).
out->write( data = itab_6a name = `itab_6a` ).
out->write( |\n| ).
out->write( data = itab_6b name = `itab_6b` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `7) SELECT list variants` ) ).
"Example 1: All fields
SELECT * FROM zdemo_abap_flsch
WHERE carrid = 'JL'
INTO TABLE @DATA(itab_7a).
"Example 2: Selected set of fields using a comma-separated list
SELECT carrid, connid, cityfrom, cityto FROM zdemo_abap_flsch
WHERE carrid = 'JL'
INTO TABLE @DATA(itab_7b).
"Example 3: Data source is explicitly specified;
"the last column in the select list is purposely specified without it; not mandatory in the case below
SELECT zdemo_abap_flsch~carrid,
zdemo_abap_flsch~connid,
zdemo_abap_flsch~airpfrom,
airpto
FROM zdemo_abap_flsch
WHERE carrid = 'JL'
INTO TABLE @DATA(itab_7c).
"Example 4: Data source is explicitly specified; all fields
SELECT zdemo_abap_flsch~*
FROM zdemo_abap_flsch
WHERE carrid = 'JL'
INTO TABLE @DATA(itab_7d).
"Example 5: Alias names defined for fields
"Data is read into a target variable declared inline
SELECT FROM zdemo_abap_flsch
FIELDS carrid AS carr,
connid AS conn,
cityfrom AS ctyfr,
cityto
WHERE carrid = 'JL'
INTO TABLE @DATA(itab_7e).
"Example 6: Data is read from a database table into an existing
"table but the line type does not match. The fields also have
"different names (but the same type). Due to the use of alias
"names, the fields are read into the corresponding fields.
TYPES: BEGIN OF struc_type_diff,
carr_id TYPE zdemo_abap_flsch-carrid,
conn_id TYPE zdemo_abap_flsch-connid,
city_from TYPE zdemo_abap_flsch-cityfrom,
city_to TYPE zdemo_abap_flsch-cityto,
END OF struc_type_diff.
DATA itab_7f TYPE TABLE OF struc_type_diff.
"In the simple case below, the addition CORRESPONDING FIELDS OF is not
"even necessary.
SELECT FROM zdemo_abap_flsch
FIELDS carrid AS carr_id,
connid AS conn_id,
cityfrom AS city_from,
cityto AS city_to
WHERE carrid = 'AZ'
INTO CORRESPONDING FIELDS OF TABLE @itab_7f.
"Example 7: Alias for the data source
SELECT ds~carrid, ds~connid
FROM zdemo_abap_flsch AS ds
WHERE carrid = 'JL'
INTO TABLE @DATA(itab_7g).
out->write( data = itab_7a name = `itab_7a` ).
out->write( |\n| ).
out->write( data = itab_7b name = `itab_7b` ).
out->write( |\n| ).
out->write( data = itab_7c name = `itab_7c` ).
out->write( |\n| ).
out->write( data = itab_7d name = `itab_7d` ).
out->write( |\n| ).
out->write( data = itab_7e name = `itab_7e` ).
out->write( |\n| ).
out->write( data = itab_7f name = `itab_7f` ).
out->write( |\n| ).
out->write( data = itab_7g name = `itab_7g` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `8) Reading from an internal table using SELECT` ) ).
"Note: The internal table from which to be read must be specified
"as host variable. The internal table should have an explicitly
"defined primary key.
DATA itab_read1 TYPE TABLE OF zdemo_abap_flsch
WITH NON-UNIQUE KEY mandt carrid connid.
"Reading from database table to fill an internal table.
SELECT FROM zdemo_abap_flsch
FIELDS mandt, carrid, connid, cityfrom, cityto
WHERE carrid = 'AA'
INTO TABLE @itab_read1.
"Reading from internal table.
SELECT FROM @itab_read1 AS itab
FIELDS *
WHERE carrid = 'AA'
INTO TABLE @DATA(itab_read2).
out->write( data = itab_read2 name = `itab_read2` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( 'INTO clause variants' ) ).
out->write( |9) UP TO: Limiting the number of returned table rows\n\n| ).
"Restricting the absolute number of returned table rows
"by specifying a number n in the addition UP TO n ROWS.
"In this case, the addition ORDER BY is also specified (but need not be specified).
"The rows of the hit list are sorted on the database server and only the number of
"sorted rows specified for UP TO n ROWS are passed to the result set. If the addition
"ORDER BY is not specified, n arbitrary rows that meet the WHERE condition are passed
"to the result set. If the ORDER BY clause does not sort the result set uniquely,
"it is not possible to define which rows are in the result set.
"Other examples here do not use the ORDER BY clause.
SELECT *
FROM zdemo_abap_flsch
WHERE carrid = 'LH'
ORDER BY carrid
INTO TABLE @DATA(itab_up)
UP TO 2 ROWS.
out->write( data = itab_up name = `itab_up` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `10) OFFSET: Returning only the table rows after a row with a specified count from the result set` ) ).
"In the example, data of all flights are retrieved, except for the 2 flights
"with the shortest flight time.
"To compare the result sets, there is one example with and one without the addition.
SELECT *
FROM zdemo_abap_flsch
WHERE carrid = 'LH'
ORDER BY fltime ASCENDING
INTO TABLE @DATA(itab_no_off).
out->write( data = itab_no_off name = `itab_no_off` ).
out->write( |\n| ).
SELECT *
FROM zdemo_abap_flsch
WHERE carrid = 'LH'
ORDER BY fltime ASCENDING
INTO TABLE @DATA(itab_w_off)
OFFSET 2.
out->write( data = itab_w_off name = `itab_w_off` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `11) Reading into individual elementary data objects` ) ).
"The field list and the INTO list must have the
"same number of elements.
"In the example, a structure and internal table are created
"to process the individually read fields within a SELECT loop.
"Structure and internal table to include the read result
DATA struct_ind TYPE struc_type.
DATA itab_ind TYPE TABLE OF struc_type.
SELECT FROM zdemo_abap_flsch
FIELDS carrid, connid, cityfrom, cityto
WHERE carrid = 'JL'
INTO (@DATA(carr_id),@DATA(conn_id),@DATA(city_from),
@DATA(city_to)).
IF sy-subrc = 0.
"Filling structure components with the individual values
struct_ind = VALUE #( carrid = carr_id
connid = conn_id
cityfrom = city_from
cityto = city_to ).
"Appending structure to internal table
APPEND struct_ind TO itab_ind.
ENDIF.
ENDSELECT.
out->write( data = itab_ind name = `itab_ind` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `12) Appending the result set to an existing internal table` ) ).
"APPEDNING TABLE
"In the example, the existing internal table has the same line type
"as the database table. The internal table from the previous
"example is used to have a table with entries.
SELECT *
FROM zdemo_abap_flsch
WHERE carrid = 'JL'
APPENDING TABLE @itab_up.
out->write( data = itab_up name = `itab_up` ).
out->write( |\n| ).
"APPENDING CORRESPONDING FIELDS OF TABLE
"In the example, the existing internal table has not a matching
"line type as the database table. First, an internal table table
"is filled using the INTO CORRESPONDING ... addition. Then, a
"statement with an APPENDING CORRESPONDING ... addition ensures
"that the existing content is kept and the target variable
"receives the read data in the corresponding fields.
DATA itab_corr TYPE TABLE OF struc_type.
"INTO CORRESPONDING FIELDS OF: Filling internal table anew
SELECT *
FROM zdemo_abap_flsch
WHERE carrid = 'LH'
INTO CORRESPONDING FIELDS OF TABLE @itab_corr
UP TO 2 ROWS.
"APPENDING CORRESPONDING FIELDS OF: Adding to existing table lines
SELECT *
FROM zdemo_abap_flsch
WHERE carrid = 'JL'
APPENDING CORRESPONDING FIELDS OF TABLE @itab_corr.
out->write( data = itab_corr name = `itab_corr` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `13) Reading into packages of a specified number of rows` ) ).
"After PACKAGE SIZE, the number of rows is specified denoting the number
"of rows to be inserted in the target object per iteration The internal
"table that is displayed shows all entries, i.e. all packages. Furthermore,
"a string table is filled and displayed to visualize the package size of each
"internal table per iteration.
DATA itab_pack TYPE TABLE OF zdemo_abap_flsch.
DATA pack_table TYPE string_table.
SELECT FROM zdemo_abap_flsch
FIELDS carrid, connid, cityfrom, cityto
WHERE carrid <> 'AZ' AND carrid <> 'DL'
INTO TABLE @DATA(itab_package)
PACKAGE SIZE 3.
IF sy-subrc = 0.
APPEND |Internal table lines processed: | &&
|{ lines( itab_package ) }| TO pack_table.
"Adding internal table content to another internal table
itab_pack = CORRESPONDING #( BASE ( itab_pack )
itab_package ).
ENDIF.
ENDSELECT.
out->write( data = pack_table name = `pack_table` ).
out->write( |\n| ).
out->write( data = itab_pack name = `itab_pack` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `14) Specifying an anonymous data object as target object` ) ).
SELECT *
FROM zdemo_abap_flsch
WHERE carrid = 'LH'
INTO TABLE NEW @DATA(dref)
UP TO 2 ROWS.
out->write( data = dref->* name = `dref->*` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Excursion: ABAP SQL - Operands and Expressions` ) ).
out->write( |15) SQL operands\n\n| ).
"SQL operands are elementary operands in an ABAP SQL statement.
"Can be database table or view columns, a literal, host variables
"(i. e. global or local data objects escaped using @) or host
"expressions (@( ... )).
"The literals can be typed (using the type name and content within
"a pair of backquotes: char`abc`) with built-in ABAP Dictionary
"types or untyped.
"Regarding host expressions: Structures and internal tables are
"possible as host expressions for statements modifying the content
"of database tables as shown further down.
"The example below demonstrates possible operands.
DATA upto 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
@upto 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(sql_operands)
"The following shows all options having the same effect
UP TO 3 ROWS. "Untyped numeric literal
"UP TO int4`3` ROWS. "Typed numerice literal
"UP TO @upto ROWS. "Host variable
"UP TO @( 10 - 7 ) ROWS. "Host expression
out->write( data = sql_operands name = `sql_operands` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `16) Numeric functions ` ) ).
"You can use built-in functions in ABAP SQL.
"Result: Value with the associated dictionary type.
"Arguments of the functions: Cover one or more SQL expressions.
SELECT SINGLE
carrname,
"Division, result rounded to an integer
div( 4, 2 ) AS div,
"Division, 3rd argument: result is rounded to the specified
"number of decimals
division( 1, 3, 2 ) AS division,
"Result is rounded to first greater integer
ceil( decfloat34`1.333` ) AS ceil,
"Result is the remainder of division
mod( 3, 2 ) AS mod,
"Result: Largest integer value not greater than the specified
"value
floor( decfloat34`1.333` ) AS floor,
"Returns the absolute number
abs( int4`-2` ) AS abs,
"Result is rounded to the specified position after the decimal
"separator
round( decfloat34`1.337`, 2 ) AS round
FROM zdemo_abap_carr
WHERE carrid = 'AA'
INTO @DATA(numeric_functions).
out->write( data = numeric_functions name = `numeric_functions` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `17) String functions` ) ).
SELECT SINGLE
carrid, "LH
carrname, "Lufthansa
url, "http://www.lufthansa.com
"Concatenates strings, ignores trailing blanks
concat( carrid, carrname ) AS concat,
"Concatenates strings, number denotes the blanks that are inserted
concat_with_space( carrid, carrname, 1 ) AS concat_with_space,
"First letter of a word -> upper case, all other letters ->
"lower case; note that a space and other special characters means
"a new word.
initcap( url ) AS initcap,
"Position of the first occurrence of the substring specified
instr( carrname,'a' ) AS instr,
"String of length n starting from the left of an expression;
"trailing blanks are ignored
left( carrname, 4 ) AS left,
"Number of characters in an expression, trailing blanks are
"ignored
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
"PCRE below: Searches a period that is followed by any character
like_regexpr( pcre = '\..',
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)
locate( carrname, 'a', 0, 2 ) AS locate,
"Searches a PCRE pattern, returns offset of match;
"many optional parameters: occurrence, case_sensitive, start,
"group
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
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
ltrim( carrname, 'L' ) AS ltrim,
"Counts all occurrences of found PCRE patterns
occurrences_regexpr( pcre = '\..',
value = url ) AS occ_regex,
"Replaces the 2nd argument with the 3rd in an expression
replace( carrname,'a','#' ) AS replace,
"Replaces a found PCRE expression;
"more parameters possible: occurrence, case_sensitive, start
replace_regexpr( pcre = '\..',
value = url,
with = '#' ) AS replace_regex,
"Extracts a string with the length specified starting from the
"right
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.
rpad( carrname, 12, '#' ) AS rpad,
"All trailing characters that match the character of the 2nd
"argument are removed; trailing blanks are removed, too
rtrim( carrname, 'a' ) AS rtrim,
"Returns a substring; 2nd argument = position from where to start;
"3rd argument: length of the extracted substring
substring( carrname, 3, 3 ) AS substring,
"Searches for a PCRE expression and returns the matched substring
"More parameters possible: occurrence, case_sensitive, start, group
substring_regexpr( pcre = '\...',
value = url ) AS substring_regexpr,
"All lower case letters are transformed to upper case letters
upper( carrname ) AS upper
FROM zdemo_abap_carr
WHERE carrid = 'LH'
INTO @DATA(string_functions).
out->write( data = string_functions name = `string_functions` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `18a) Special functions` ) ).
SELECT SINGLE
carrid,
"Conversion functions
"When used: Special conversions that cannot be handled in a
"general CAST expression
"Type conversion: string of fixed length (e.g. of type c) to
"variable length string of type string
to_clob( carrid ) AS clob,
"Byte string -> character string
bintohex( raw`3599421128650F4EE00008000978B976` ) AS bintohex,
"Character string -> byte string
hextobin( char`3599421128650F4EE00008000978B976` ) AS hextobin,
"Byte field of type RAW to a byte string (BLOB) of type RAWSTRING
to_blob( raw`3599421128650F4EE00008000978B976` ) AS blob,
"Unit conversion function
"More parameters are available.
"Converts miles to kilometers
unit_conversion( quantity = d34n`1`,
source_unit = unit`MI`,
target_unit = unit`KM` ) AS miles_to_km,
"Date and time functions
"There are plenty of functions; the below functions are a
"selection.
add_days( @( cl_abap_context_info=>get_system_date( ) ), 4
) AS add_days,
add_months( @( cl_abap_context_info=>get_system_date( ) ), 2
) AS add_months,
is_valid( @( cl_abap_context_info=>get_system_date( ) ) ) AS date_is_valid,
is_valid( @( cl_abap_context_info=>get_system_time( ) ) ) AS time_is_valid
FROM zdemo_abap_carr
WHERE carrid = 'AA'
INTO @DATA(special_functions).
"Retrieving type information using RTTI to demonstrate the effect
"of type conversions like to_clob etc.
"type_kind: g (character string with variable length),
"C (character string of fixed length), X (binary), y (byte string)
DATA(components) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_data( special_functions )
)->components.
out->write( data = components name = `components` ).
out->write( |\n| ).
out->write( data = special_functions name = `special_functions` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `18b) coalesce Function` ) ).
"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).
out->write( data = join_w_null name = `join_w_null` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `19) Aggregate Expressions` ) ).
"Consist of aggregate functions and aggregate the values of
"multiple rows of the result set of a query into a single value.
"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).
out->write( data = agg_exp name = `agg_exp` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `20a) Arithmetic Expressions` ) ).
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).
out->write( data = arithmetic_sql_expr name = `arithmetic_sql_expr` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `20b) Cast Expressions` ) ).
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).
out->write( data = cast_expr name = `cast_expr` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `20c) String Expressions` ) ).
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).
out->write( data = string_expr name = `string_expr` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `20d) Case Expressions` ) ).
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).
out->write( data = case_expr name = `case_expr` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `20e) Null Expressions` ) ).
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).
out->write( data = null_expr name = `null_expr` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `21) Window expressions (1)` ) ).
"A simple window is constructed in the OVER clause,
"window functions - here aggregate functions - are applied.
"To narrow the entries in the resulting table, duplicates
"are deleted.
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).
DELETE ADJACENT DUPLICATES FROM win COMPARING ALL FIELDS.
out->write( data = win name = `win` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `22) Window expressions (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 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. A better use case as below example 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(win_order).
out->write( data = win_order name = `win_order` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `SQL conditions` ) ).
out->write( |23) SQL conditions (1)\n\n| ).
"The example demonstrates a WHERE clause with =, >, <, <=, >=, AND
SELECT * FROM zdemo_abap_fli
WHERE carrid = 'LH' "or EQ
AND price > 700 "or GT
AND seatsocc < 320 "or LT
AND seatsmax <= 330 "or LE
AND seatsmax_b >= 30 "or GE
INTO TABLE @DATA(itab_comp_op).
out->write( data = itab_comp_op name = `itab_comp_op` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `24) SQL conditions (2)` ) ).
"The example demonstrates a WHERE clause with
"BETWEEN, NOT BETWEEN, OR
SELECT * FROM zdemo_abap_fli
WHERE seatsmax BETWEEN 350 AND 400 "#EC CI_CMPLX_WHERE
OR price NOT BETWEEN 100 AND 1500
INTO TABLE @DATA(it_sql_cond).
out->write( data = it_sql_cond name = `it_sql_cond` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `25) SQL conditions (3)` ) ).
"The example demonstrates a WHERE clause with character literals:
"- LIKE '%FRAN%': Condition is true if the column cityfrom contains
" a string containing the pattern 'FRAN'
"- NOT LIKE '_X%': Condition is true if the column airpto contains
" a value whose second character is not 'X'.
"- IN ( ... ): condition is true if the column cityto contains
" one of the values specified within the brackets
"- NOT IN ( ... ): condition is true if the column cityto does not
" contain one of the values specified within the brackets
SELECT * FROM zdemo_abap_flsch
WHERE cityfrom LIKE '%FRAN%'
AND airpto NOT LIKE '_X%'
AND cityto IN ( 'BERLIN', 'NEW YORK', 'LONDON' )
AND cityto NOT IN ( 'SYDNEY' )
INTO TABLE @DATA(itab_like_in).
out->write( data = itab_like_in name = `itab_like_in` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `25b) SQL conditions (4)` ) ).
"---- SQL conditions demonstrated with the WHERE clause ----
"Note:
"- For most of the self-contained examples, an internal table is used as the
" data source of SELECT statements to work with simple data.
"- For some examples that are covered, such as subqueries, demo database tables
" from the cheat sheet repository are used in addition.
"- Dynamic specifications are also possible. They are not covered here. See
" the Dynamic Programming cheat sheet.
"---- Types and internal table to work with in the examples ----
"Note: You cannot use type string columns in WHERE conditions.
TYPES: BEGIN OF demo_struc,
id TYPE i,
name TYPE c LENGTH 15,
"name TYPE string,
END OF demo_struc.
DATA itab_sql_cond TYPE SORTED TABLE OF demo_struc WITH UNIQUE KEY id.
"Populating internal table with data to work with in the examples
itab_sql_cond = VALUE #( ( id = 1 name = 'bear' )
( id = 2 name = 'camel' )
( id = 3 name = 'rabbit' )
( id = 4 name = 'zebra' )
( id = 5 name = 'dog' )
( id = 6 name = 'deer' )
( id = 7 name = 'squirrel' )
( id = 8 name = 'cheetah' )
( id = 9 name = 'elephant' )
( id = 10 name = 'donkey' )
( id = 11 name = 'fish' )
( id = 12 name = 'sheep' ) ).
"---- =, <>, >, >= (as a selection of possible comparison operators) ----
SELECT id FROM @itab_sql_cond AS tab WHERE name = 'bear' INTO TABLE @DATA(it).
SELECT id FROM @itab_sql_cond AS tab WHERE name <> 'bear' INTO TABLE @it.
SELECT id FROM @itab_sql_cond AS tab WHERE id > 10 INTO TABLE @it.
SELECT id FROM @itab_sql_cond AS tab WHERE id >= 10 INTO TABLE @it.
"---- Combining logical expressions using AND, OR and parentheses ----
SELECT id FROM @itab_sql_cond AS tab WHERE id = 1 AND name = 'bear' INTO TABLE @it.
SELECT id FROM @itab_sql_cond AS tab WHERE name = 'bear' OR name = 'sheep' INTO TABLE @it.
"In the following example, the resulting table is initial. One of the expressions
"in parentheses is false (AND is used between the expressions in parentheses).
"In contrast, the example below returns an entry because of using OR.
SELECT id FROM @itab_sql_cond AS tab
WHERE ( id = 1 AND name = 'bear' )
AND ( id = 20 AND name = 'camel' )
INTO TABLE @it.
SELECT id FROM @itab_sql_cond AS tab
WHERE ( id = 1 AND name = 'bear' )
OR ( id = 20 AND name = 'camel' )
INTO TABLE @it.
"------------------------ [NOT] BETWEEN ------------------------
SELECT id FROM @itab_sql_cond AS tab WHERE id BETWEEN 1 AND 4 INTO TABLE @it.
"The condition with BETWEEN above corresponds to the following condition.
"The example makes use of a condition specified in parentheses to combine multiple
"expressions.
SELECT id FROM @itab_sql_cond AS tab WHERE ( id >= 1 AND id <= 4 ) INTO TABLE @it.
"Negation with NOT
SELECT id FROM @itab_sql_cond AS tab WHERE id NOT BETWEEN 1 AND 4 INTO TABLE @it.
"------------------------ IS [NOT] INITIAL ------------------------
SELECT id FROM @itab_sql_cond AS tab WHERE id IS NOT INITIAL INTO TABLE @it.
SELECT id FROM @itab_sql_cond AS tab WHERE id IS INITIAL INTO TABLE @it.
"------------------------ [NOT] LIKE ------------------------
"For (not) matching a specified pattern
"Note: % (any character string), _ (any character).
SELECT name FROM @itab_sql_cond AS tab
WHERE name LIKE '%ee%'
OR name LIKE '_o%'
INTO TABLE @DATA(names). "dog,deer,cheetah,donkey,sheep
SELECT name FROM @itab_sql_cond AS tab
WHERE name NOT LIKE '%ee%'
INTO TABLE @names.
"ESCAPE addition for defining a single-character escape character
"In the following example, this character is #. It is placed before
"the % character in the specification after LIKE. In this case, %
"is escaped and does then not stand for any character string in the
"evaluation.
"Adding a table entry for this syntax example.
itab_sql_cond = VALUE #( BASE itab_sql_cond ( id = 13 name = '100%' ) ).
"Any character sequence followed by the % character
SELECT name FROM @itab_sql_cond AS tab
WHERE name LIKE '%#%' ESCAPE '#'
INTO TABLE @names.
"Deleting the entry because it is not relevant for the further examples.
DELETE itab_sql_cond INDEX 13.
"------------------------ [NOT] IN (using a value set) ------------------------
"For (not) matching a value in a set of values specified in parentheses.
"Single operands on the left side of IN
SELECT id FROM @itab_sql_cond AS tab
WHERE name IN ( 'camel', 'rabbit', 'dog', 'snake' )
INTO TABLE @it.
"Negation NOT IN; note to use host variables/expressions for local/global data objects
DATA(animal) = 'sheep'.
SELECT id FROM @itab_sql_cond AS tab
WHERE name NOT IN ( 'fish', @animal )
INTO TABLE @it.
"Operand list (a parenthesized comma-separated list) on the left side of IN
"For (not) matching value tuples from a set of value tuples specified in parentheses on the right side.
"In the following example, two values are specified in the operand list on the left. Consequently,
"two values with appropriate types must be specified in parentheses on the right.
SELECT id FROM @itab_sql_cond AS tab
WHERE ( id, name ) IN ( ( 1, 'bear' ), ( 3, 'rabbit' ), ( 8, 'zebra' ), ( 20, 'dog' ) )
INTO TABLE @it.
"------------------------ [NOT] IN (using a subquery) ------------------------
"[NOT] IN for matching a value contained in the result set of a subquery
"In the following example, the subquery reads data from a demo database table.
"For a representative result, the table is cleared, and then filled with 'suitable'
"data sets.
DELETE FROM zdemo_abap_tab1.
MODIFY zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 11 num1 = 11 )
( key_field = 12 num1 = 12 )
( key_field = 13 num1 = 13 )
( key_field = 14 num1 = 14 ) ) ).
SELECT id FROM @itab_sql_cond AS tab
WHERE id IN ( SELECT key_field FROM zdemo_abap_tab1 ) INTO TABLE @it.
"------------------------ [NOT] IN (using a ranges table) ------------------------
"[NOT] IN for checking whether the operands on the left side match a ranges condition in a ranges table
"Declaring a ranges table
DATA rangestab TYPE RANGE OF i.
"Populating a ranges table using the VALUE operator
rangestab = VALUE #( ( sign = 'I' option = 'BT' low = 1 high = 3 )
( sign = 'I' option = 'GE' low = 10 ) ).
SELECT id FROM @itab_sql_cond AS tab WHERE id IN @rangestab INTO TABLE @it.
"You cannot use logical operators such as CP (conforms to pattern) in the WHERE clause.
"In a ranges table, they are possible.
"Note:
"- Regarding CP: * (any character sequence), + (any character), # (escape character)
"- An equivalent example above uses the LIKE addition.
DATA rt TYPE RANGE OF demo_struc-name.
rt = VALUE #( ( sign = 'I' option = 'CP' low = '*ee*' ) "ee in a string
( sign = 'I' option = 'CP' low = '+o*' ) ). "o in second position
SELECT name FROM @itab_sql_cond AS tab
WHERE name IN @rt
INTO TABLE @names.
"------------------------ EXISTS ------------------------
"For checking the result set of a subquery.
"The following example reads all entries from the internal table if entries having
"the same key also exist in the database table.
"Note: The SELECT list in the subquery only contains a literal to determine that
"the entry exists. Specifying explicit column names is not relevant.
SELECT id FROM @itab_sql_cond AS tab WHERE
EXISTS ( SELECT @abap_true FROM zdemo_abap_tab1 WHERE key_field = tab~id )
INTO TABLE @it.
"------------------------ IS [NOT] NULL ------------------------
"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 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 WHERE clause uses the addition IS NULL.
"Therefore, the result only contains this entry. char2 is assigned the type-initial
"value in the result.
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
WHERE tab1~char1 IS NULL
INTO TABLE @DATA(joined_tab).
"The following example visualizes the null values. The INDICATORS addition of the
"INTO clause 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.
"For more information on the syntax, refer to the ABAP Keyword Documentation.
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.
"Negation IS NOT NULL
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
WHERE tab1~char1 IS NOT NULL
INTO TABLE @joined_tab.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Further clauses in SELECT statements` ) ).
out->write( |26) GROUP BY: Combining groups of table rows in the result set\n\n| ).
"In the example, the database table rows that have the same content
"in column CARRID are combined. The lowest and highest values in
"column PRICE are determined for each of these groups and placed
"into the combined row. 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. Aggregate functions in aggregate expressions are an
"exception, as shown below.
SELECT FROM zdemo_abap_fli
FIELDS carrid,
MIN( price ) AS min_price,
MAX( price ) AS max_price
GROUP BY carrid
INTO TABLE @DATA(itab_gr).
out->write( data = itab_gr name = `itab_gr` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `27) HAVING: Limiting the number of rows` &&
` in groups in the result set` ) ).
"The addition HAVING limits the number of rows in groups in the
"result set of a query by using a logical expression on these rows.
"The logical expression evaluates the content of row groups. Those
"rows are placed in the result set for which the logical expression
"is true.
SELECT FROM zdemo_abap_flsch
FIELDS carrid, connid, cityfrom, cityto
WHERE carrid = 'LH'
GROUP BY carrid, connid, cityfrom, cityto
HAVING SUM( fltime ) > 100
INTO TABLE @DATA(itab_hav).
out->write( data = itab_hav name = `itab_hav` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `28) ORDER BY: 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. You can also
"order by any columns and by explicitly specifying the sort order.
"There are more options to order, for example, by using SQL
"expressions.
"Example 1: Sorting the result set by primary key
SELECT *
FROM zdemo_abap_flsch
WHERE carrid <> 'UA'
ORDER BY PRIMARY KEY
INTO TABLE @DATA(itab_ord1)
UP TO 3 ROWS.
"Example 2: Sorting by arbitrary field and specifying the sort order
SELECT *
FROM zdemo_abap_flsch
WHERE carrid <> 'UA'
ORDER BY fltime DESCENDING
INTO TABLE @DATA(itab_ord2)
UP TO 3 ROWS.
out->write( data = itab_ord1 name = `itab_ord1` ).
out->write( |\n| ).
out->write( data = itab_ord2 name = `itab_ord2` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `WHERE clause variants: Selecting data by evaluating the content of other tables` ) ).
out->write( |29) FOR ALL ENTRIES addition\n\n| ).
"In the example, only those entries should be read from the
"database table if entries exist in the internal table that meet
"the conditions specified in the WHERE clause. Note that you should
"ensure that the internal table is not initial.
"Check the ABAP Keyword Documentation for various restrictions that
"apply when using this addition. The following example is just for
"demonstrating the syntax - as are all examples.
"Filling an internal table
SELECT * FROM zdemo_abap_fli
WHERE seatsmax < 300
INTO TABLE @DATA(cond_tab).
IF ( 0 < lines( cond_tab ) ).
SELECT carrid, connid, cityfrom, cityto "#EC CI_NO_TRANSFORM
FROM zdemo_abap_flsch
FOR ALL ENTRIES IN @cond_tab
WHERE carrid = @cond_tab-carrid
AND connid = @cond_tab-connid
INTO TABLE @DATA(itab_forall).
ENDIF.
out->write( data = itab_forall name = `itab_forall` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `30) Checking the result set of a subquery` ) ).
"In the example, all available flights leaving from a city with
"FRAN in the name (San Francisco, Frankfurt) existing in another
"database table and for which further comparisons are true
"(matching CARRID and CONNID) are read into an internal table.
SELECT carrid, connid, fldate
FROM zdemo_abap_fli AS zdemo_abap_fli
WHERE EXISTS
( SELECT carrid FROM zdemo_abap_flsch
WHERE carrid = zdemo_abap_fli~carrid
AND connid = zdemo_abap_fli~connid
AND cityfrom LIKE '%FRAN%' )
ORDER BY carrid, connid, fldate
INTO TABLE @DATA(itab_sub).
out->write( data = itab_sub name = `itab_sub` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Combining Data of Multiple Database Tables` ) ).
out->write( |31) Inner join\n\n| ).
"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.
"Example 1
SELECT p~carrid, p~connid, p~cityto, f~fldate
FROM zdemo_abap_flsch AS p
INNER JOIN zdemo_abap_fli AS f
ON p~carrid = f~carrid AND p~connid = f~connid
WHERE p~cityfrom = 'NEW YORK'
ORDER BY p~carrid
INTO TABLE @DATA(itab_in1).
"Example 2: Multiple inner joins
SELECT p~carrid, s~carrname, p~connid, p~cityto, f~fldate
FROM zdemo_abap_flsch AS p
INNER JOIN zdemo_abap_fli AS f
ON p~carrid = f~carrid AND p~connid = f~connid
INNER JOIN zdemo_abap_carr AS s
ON p~carrid = s~carrid
WHERE p~cityfrom = 'FRANKFURT'
ORDER BY p~carrid
INTO TABLE @DATA(itab_in2).
out->write( data = itab_in1 name = `itab_in1` ).
out->write( |\n| ).
out->write( data = itab_in2 name = `itab_in2` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `32) Left outer join` ) ).
"Result set:
"- Same result set as the inner join.
"- Difference: For each selected row on the left side as LEFT 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.
"In the example, all rows from the left-hand side (zdemo_abap_carr)
"are returned as well as the matching rows from the right-hand side
"(zdemo_abap_flsch). However, the ON condition
""p~cityfrom = 'FRANKFURT'" is not met for several entries in
"zdemo_abap_flsch and a CONNID does not exist.
SELECT s~carrid, s~carrname, p~connid
FROM zdemo_abap_carr AS s
LEFT OUTER JOIN zdemo_abap_flsch AS p
ON s~carrid = p~carrid AND p~cityfrom = 'FRANKFURT'
WHERE s~carrid <> 'UA'
ORDER BY s~carrid
INTO TABLE @DATA(itab_lo).
out->write( data = itab_lo name = `itab_lo` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `33) Merging the result sets of multiple queries into a single result set using UNION` ) ).
"Effect: The rows of the result set of the query after UNION are
"inserted into the result set of the query in front of UNION.
"The example demonstrates the union of two tables and
"visualizes those columns that do not exist in the other table by
"setting the value '-'. Here, a CAST is required for the column CONNID.
SELECT FROM zdemo_abap_carr
FIELDS carrname,
CAST( '-' AS CHAR( 4 ) ) AS connid,
'-' AS cityfrom,
'-' AS cityto
WHERE carrid = 'LH'
UNION
SELECT FROM zdemo_abap_flsch
FIELDS '-' AS carrname,
CAST( connid AS CHAR( 4 ) ) AS connid,
cityfrom,
cityto
WHERE carrid = 'LH'
ORDER BY carrname DESCENDING, connid, cityfrom, cityto
INTO TABLE @DATA(itab_union).
out->write( data = itab_union name = `itab_union` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `33b) Returning distinct rows of a result set using INTERSECT` ) ).
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).
out->write( data = itab_no_intersect name = `itab_no_intersect` ).
out->write( |\n| ).
"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).
out->write( data = itab_no_intersect name = `itab_w_intersect` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `33c) Returning distinct rows of a result set using EXCEPT` ) ).
"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_except) = 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_except AS it
INNER JOIN zdemo_abap_carr ON zdemo_abap_carr~carrid = it~carrid
ORDER BY carrid ASCENDING
INTO TABLE @DATA(itab_w_except).
out->write( data = itab_w_except name = `itab_w_except` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `34) Common Table Expressions (CTE) (1)` ) ).
"The result sets of both common table expressions +connections
"and +sum_seats are merged in the subquery of the CTE +result in
"a join expression. An explicit name list assigns names to the
"resulting columns. These names are used in the main query to sort
"the results. For each flight connection of the selected airline,
"the total number of occupied seats is output from the database
"table.
WITH
+connections AS (
SELECT zdemo_abap_flsch~carrid, carrname, connid, cityfrom, cityto
FROM zdemo_abap_flsch
INNER JOIN zdemo_abap_carr
ON zdemo_abap_carr~carrid = zdemo_abap_flsch~carrid
WHERE zdemo_abap_flsch~carrid BETWEEN 'AA' AND 'JL' ),
+sum_seats AS (
SELECT carrid, connid, SUM( seatsocc ) AS sum_seats
FROM zdemo_abap_fli
WHERE carrid BETWEEN 'AA' AND 'JL'
GROUP BY carrid, connid ),
+result( name, connection, departure, arrival, occupied ) AS (
SELECT carrname, c~connid, cityfrom, cityto, sum_seats
FROM +connections AS c
INNER JOIN +sum_seats AS s
ON c~carrid = s~carrid AND
c~connid = s~connid )
SELECT *
FROM +result
ORDER BY name, connection
INTO TABLE @DATA(itab_cte).
out->write( data = itab_cte name = `itab_cte` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `35) CTE and a SELECT Loop (2)` ) ).
"The example shows a WITH statement, whose main query creates a
"tabular result set. Since the data is written into work area
"rather than to an internal table, a SELECT loop is opened, which
"must be closed with ENDWITH.
WITH
+carriers AS ( SELECT FROM zdemo_abap_carr
FIELDS carrid, carrname )
SELECT FROM zdemo_abap_flsch AS s
INNER JOIN +carriers AS c
ON s~carrid = c~carrid
FIELDS c~carrname, s~connid
WHERE s~carrid = 'LH'
INTO @DATA(wa_cte_loop)
UP TO 3 ROWS.
out->write( data = wa_cte_loop name = `wa_cte_loop` ).
out->write( |\n| ).
ENDWITH.
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `Changing data in database tables` ) ).
"Deleting database table to work with
DELETE FROM zdemo_abap_carr.
"Creating table rows to be inserted in the database table
DATA(row1) = VALUE zdemo_abap_carr( carrid = 'AF'
carrname = 'Air France'
currcode = 'EUR'
url = 'http://www.airfrance.fr' ).
DATA(row2) = VALUE zdemo_abap_carr( carrid = 'UA'
carrname = 'United Airlines'
currcode = 'USD'
url = 'http://www.ual.com' ).
**********************************************************************
out->write( |36) INSERT: Inserting individual line into a database table\n\n| ).
"Inserting from an existing structure
INSERT INTO zdemo_abap_carr VALUES @row1.
"Alternative syntax having the same effect as the statement above
INSERT zdemo_abap_carr FROM @row2.
"Inserting from a structure created inline using a
"constructor expression with VALUE within a host expression
INSERT zdemo_abap_carr FROM @( VALUE #( carrid = 'SR'
carrname = 'Swiss'
currcode = 'CHF'
url = 'http://www.swiss.com' ) ).
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `37) INSERT: Inserting multiple rows into a database table` ) ).
"Creating and filling an internal table
DATA itab_insert TYPE TABLE OF zdemo_abap_carr.
itab_insert = VALUE #( ( carrid = 'BA'
carrname = 'British Airways'
currcode = 'GBP'
url = 'http://www.british-airways.com' )
( carrid = 'FJ'
carrname = 'Air Pacific'
currcode = 'USD'
url = 'http://www.airpacific.com' ) ).
"Inserting from existing internal table
INSERT zdemo_abap_carr FROM TABLE @itab_insert.
"Inserting from an internal table created inline using
"a constructor expression with VALUE within a host expression
INSERT zdemo_abap_carr FROM TABLE @( VALUE #( ( carrid = 'ET'
carrname = 'Ethiopian Airlines'
currcode = 'ETB'
url = 'http://www.ethiopianairlines.com' )
( carrid = 'QF'
carrname = 'Qantas Airways'
currcode = 'AUD'
url = 'http://www.qantas.com.au' ) ) ).
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `38) INSERT: Inserting multiple rows into a database table accepting duplicate keys` ) ).
"ACCEPTING DUPLICATE KEYS addition: To avoid a runtime error when
"inserting entries from an internal table having duplicate keys,
"all lines that would produce duplicate entries in the database
"table regarding the keys are discarded and sy-subrc is set to 4.
"Creating and filling an internal table
DATA itab_adk TYPE TABLE OF zdemo_abap_carr.
itab_adk = VALUE #( ( carrid = 'SQ'
carrname = 'Singapore Airlines'
currcode = 'SGD'
url = 'http://www.singaporeair.com' )
( carrid = 'SQ'
carrname = 'Singapore Airlines'
currcode = 'SGD'
url = 'http://www.singaporeair.com' ) ).
INSERT zdemo_abap_carr FROM TABLE @itab_adk ACCEPTING DUPLICATE KEYS.
DATA(subrc) = sy-subrc.
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
out->write( |\n| ).
out->write( data = subrc name = `subrc` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `39) INSERT: Using a subquery` ) ).
"The purpose of this abstract example is just to visualize that
"subqueries are possible in INSERT statements. In the example,
"the goal is just to get one entry from table zdemo_abap_flsch.
"Since only MANDT and CARRID are shared fields in the two database
"tables other values in zdemo_abap_carr remain empty. The line is
"further processed in the following example.
INSERT zdemo_abap_carr FROM ( SELECT carrid
FROM zdemo_abap_flsch
WHERE carrid = 'LH' AND
connid = '0400' ).
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `40) UPDATE: Changing content of existing rows` ) ).
"Creating and filling structure
"In the case below, all field values except the key field are updated.
DATA(row_upd) = VALUE zdemo_abap_carr(
carrid = 'LH'
carrname = 'Lufthansa'
currcode = 'EUR'
url = 'http://www.lufthansa.com' ).
"Creating and filling internal table
DATA itab_update LIKE itab_insert.
itab_update = VALUE #( ( carrid = 'BA'
carrname = 'British Airways'
currcode = 'GBP'
url = 'http://www.britishairways.com' ) "updated
( carrid = 'FJ'
carrname = 'Fiji Airways' "updated
currcode = 'USD'
url = 'http://www.fijiairways.com' ) )."updated
UPDATE zdemo_abap_carr FROM @row_upd.
UPDATE zdemo_abap_carr FROM TABLE @itab_update.
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `41) UPDATE: Changing values of specific fields in all table rows` ) ).
"Using the SET addition, you can change the values of specific
"fields in all table rows without overwriting existing values in
"other fields.
"In the example, the field CURRCODE is set as specified for all
"rows for which the WHERE condition is true.
UPDATE zdemo_abap_carr
SET currcode = 'EUR'
WHERE carrid <> 'UA' AND carrid <> 'ET'.
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `42) INDICATORS addition to UPDATE statements: ` &&
`Changing values of specific fields without overwriting ` &&
`existing values of other fields ` ) ).
"Example:
"- Structured type is created with WITH INDICATORS addition
"- Internal table from which to update a database table is created; it
" includes the indicator structure comp_ind
"- Internal table is filled; only one component is flagged as
" to be updated
"- Other fields remain unchanged; note that key fields must be
" included in ind_tab (indicator setting for key fields has
" no effect)
"Structured type with WITH INDICATORS addition
TYPES ind_wa TYPE zdemo_abap_carr WITH INDICATORS comp_ind TYPE abap_bool.
DATA ind_tab TYPE TABLE OF ind_wa.
"Filling internal table; only CURRCODE should be updated
ind_tab = VALUE #( ( carrid = 'QF'
carrname = 'Qantas Airways'
currcode = 'AUD'
comp_ind-currcode = abap_true )
( carrid = 'SQ'
carrname = 'Singapore Airlines'
currcode = 'SGD'
comp_ind-currcode = abap_true ) ).
UPDATE zdemo_abap_carr
FROM TABLE @ind_tab
INDICATORS SET STRUCTURE comp_ind.
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `43) MODIFY: Inserting and changing rows` ) ).
"The example only uses host expressions.
"Modifying an entry based on a row. Here, a new entry is created in
"the database table since no row exists having the key.
"The example uses a structure created inline using a constructor
"expression with VALUE within a host expression.
MODIFY zdemo_abap_carr FROM @(
VALUE #( carrid = 'AZ'
carrname = 'ITA Airways'
currcode = 'EUR'
url = 'http://www.ita-airways.com' ) ).
"Storing the value of system field sy-dbcnt to determine how many
"table rows were modified.
DATA(dbcnt) = sy-dbcnt.
"Modifying entries based on an internal table. Here, no new entry
"is created. Existing entries are updated.
MODIFY zdemo_abap_carr FROM TABLE @( VALUE #( ( carrid = 'BA'
carrname = 'British Airways'
currcode = 'GBP' "modified
url = 'http://www.britishairways.co.uk' ) "mod
( carrid = 'QF'
carrname = 'Qantas Airways'
currcode = 'AUD'
url = 'http://www.qantas.com' ) ) ). "modified
"Adding the value of sy-dbcnt to the value from above to get
"the total number of modified rows in this example.
dbcnt = dbcnt + sy-dbcnt.
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
out->write( |\n| ).
out->write( |{ dbcnt } table rows were modified.| ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `44) DELETE: Deleting table rows` ) ).
"Note that you specify the key fields only.
"Deleting an entry based on a row. Here, the example uses a
"structure created inline and by only specifying the key value
"using a constructor expression with VALUE within a host
"expression.
DELETE zdemo_abap_carr FROM @( VALUE #( carrid = 'QF' ) ).
"Deleting multiple entries based on an internal table.
"Same as above, the internal table is created inline.
DELETE zdemo_abap_carr FROM TABLE @( VALUE #( ( carrid = 'AF' )
( carrid = 'AZ' )
( carrid = 'LH' ) ) ).
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `45) DELETE: Deleting table rows based on a condition` ) ).
DELETE FROM zdemo_abap_carr WHERE currcode <> 'EUR'.
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `46) DELETE: Delete complete table` ) ).
DELETE FROM zdemo_abap_carr.
select_from_dbtab( ).
out->write( data = itab_res name = `itab_res` ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `47) Exploring ABAP SQL statements using constructor expressions created in place` ) ).
TYPES it_type TYPE TABLE OF zdemo_abap_tab1 WITH EMPTY KEY.
DELETE FROM zdemo_abap_tab1.
"--- VALUE ---
"VALUE operator as shown above, creating an internal table in place
INSERT zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 1 char1 = 'aaa' char2 = 'bbb' num1 = 10 num2 = 100 )
( key_field = 2 char1 = 'ccc' char2 = 'ddd' num1 = 20 num2 = 200 ) ) ).
"FOR LOOP with VALUE
DATA(it_f) = VALUE it_type( ( key_field = 3 char1 = 'ee' char2 = 'ff' num1 = 30 num2 = 300 )
( key_field = 4 char1 = 'gg' char2 = 'hh' num1 = 40 num2 = 400 )
( key_field = 5 char1 = 'ii' char2 = 'jj' num1 = 50 num2 = 500 ) ).
"In the example, the internal table from above is looped across. The index value is
"stored and used to modify field values of the internal table. In doing so, the modified
"internal table values are inserted into the database table.
INSERT zdemo_abap_tab1 FROM TABLE @( VALUE #( FOR wa IN it_f INDEX INTO idx ( key_field = wa-key_field
char1 = wa-char1 && idx
char2 = wa-char2 && idx
num1 = wa-num1 + idx
num2 = wa-num2 + idx ) ) ).
"Using a constructor expression with VALUE and BASE in an UPDATE statement
"The example assumes selecting an entry from a database, modifying it, and updating it again,
"but the non-modified entries shall remain unchanged.
INSERT zdemo_abap_tab1 FROM @( VALUE #( key_field = 100 char1 = 'xxx' char2 = 'yyy' num1 = 100 num2 = 101 ) ).
SELECT SINGLE * FROM zdemo_abap_tab1 WHERE key_field = 100 INTO @DATA(read_line).
UPDATE zdemo_abap_tab1 FROM @( VALUE #( BASE read_line char2 = '#' num1 = 1 ) ).
"--- CORRESPONDING ---
TYPES: BEGIN OF s1,
key_field TYPE i,
char1 TYPE c LENGTH 5,
num1 TYPE i,
END OF s1,
it_type_s1 TYPE TABLE OF s1 WITH EMPTY KEY,
BEGIN OF s2,
key TYPE i,
char TYPE c LENGTH 5,
number1 TYPE i,
num2 TYPE p LENGTH 8 DECIMALS 2,
END OF s2,
it_type_s2 TYPE TABLE OF s2 WITH EMPTY KEY.
"Identical component names in the internal table
"The example includes compatible and convertible types.
DATA(it_g) = VALUE it_type_s1( ( key_field = 6 char1 = 'kkk' num1 = 60 )
( key_field = 7 char1 = 'lll' num1 = 70 ) ).
INSERT zdemo_abap_tab1 FROM TABLE @( CORRESPONDING #( it_g ) ).
"Non-identical component names in the internal table; using the MAPPING/EXCEPT additions
"The example includes compatible and convertible types.
DATA(it_h) = VALUE it_type_s2( ( key = 8 char = 'mmm' number1 = 80 num2 = '1.23' )
( key = 9 char = 'nnn' number1 = 90 num2 = '4.56' ) ).
INSERT zdemo_abap_tab1 FROM TABLE @( CORRESPONDING #( it_h MAPPING key_field = key char2 = char num1 = number1 EXCEPT num2 ) ).
SELECT * FROM zdemo_abap_tab1 INTO TABLE @DATA(itab_constr).
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `48) Evaluating ABAP System Fields after ABAP SQL Statements` ) ).
"Clearing a demo database table
DELETE FROM zdemo_abap_tab1.
"--------------------- INSERT ---------------------
INSERT zdemo_abap_tab1 FROM @( VALUE #( key_field = 1 ) ).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 1.
INSERT zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 2 )
( key_field = 3 ) ) ).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 2.
INSERT zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 2 )
( key_field = 3 ) ) ) ACCEPTING DUPLICATE KEYS.
ASSERT sy-subrc = 4.
ASSERT sy-dbcnt = 0.
INSERT zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 3 )
( key_field = 4 ) ) ) ACCEPTING DUPLICATE KEYS.
ASSERT sy-subrc = 4.
ASSERT sy-dbcnt = 1.
"--------------------- UPDATE ---------------------
UPDATE zdemo_abap_tab1 FROM @( VALUE #( key_field = 1 num1 = 1 ) ).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 1.
UPDATE zdemo_abap_tab1 FROM @( VALUE #( key_field = 9999 num1 = 9999 ) ).
ASSERT sy-subrc = 4.
ASSERT sy-dbcnt = 0.
UPDATE zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 2 num1 = 2 )
( key_field = 3 num1 = 3 ) ) ).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 2.
UPDATE zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 4 num1 = 4 )
( key_field = 1111 num1 = 1111 ) ) ).
ASSERT sy-subrc = 4.
ASSERT sy-dbcnt = 1.
"--------------------- MODIFY ---------------------
MODIFY zdemo_abap_tab1 FROM @( VALUE #( key_field = 1 num1 = 11 ) ).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 1.
MODIFY zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 2 num1 = 22 ) "Entry modified
( key_field = 5 num1 = 5 ) ) ). "Entry inserted
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 2.
"--------------------- SELECT ---------------------
SELECT *
FROM zdemo_abap_tab1
INTO TABLE @DATA(tab).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 5.
ASSERT sy-dbcnt = lines( tab ).
SELECT *
FROM zdemo_abap_tab1
WHERE key_field <= 3
INTO TABLE @DATA(tab2).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 3.
SELECT *
FROM zdemo_abap_tab1
WHERE key_field > 10
INTO TABLE @DATA(tab3).
ASSERT sy-subrc = 4.
ASSERT sy-dbcnt = 0.
"--------------------- DELETE ---------------------
DELETE zdemo_abap_tab1 FROM @( VALUE #( key_field = 1 ) ).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 1.
DELETE zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 1 ) "Entry not existent
( key_field = 2 )
( key_field = 3 ) ) ).
ASSERT sy-subrc = 4.
ASSERT sy-dbcnt = 2.
DELETE FROM zdemo_abap_tab1 WHERE key_field >= 5.
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 1.
"Only one entry left in the database table
DELETE FROM zdemo_abap_tab1.
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 1.
SELECT *
FROM zdemo_abap_tab1
INTO TABLE @DATA(tab4).
ASSERT sy-subrc = 4.
ASSERT sy-dbcnt = 0.
out->write( zcl_demo_abap_aux=>no_output ).
**********************************************************************
out->write( zcl_demo_abap_aux=>heading( `49) Typed literals` ) ).
"Miscellaneous typed literals in an ABAP SQL statement
"Note that typed literals can be specified in read
"positions where host variables are possible.
DATA(tmstamp) = CONV timestamp( '20240808112517' ).
DATA(some_string) = `Some string`.
SELECT SINGLE
FROM zdemo_abap_fli
FIELDS
carrid,
@some_string AS host_var,
char`X` AS flag,
int8`32984723948723` AS int8,
raw`11` AS raw,
numc`1234` AS numc,
utclong`2024-01-01T10:01:02,2` AS utc,
tims`101507` AS tims,
curr`173.95` AS curr,
"Multiple cast expressions splitting a time stamp into date and time parts
CAST( CAST( div( @tmstamp, 1000000 ) AS CHAR ) AS DATS ) AS date,
CAST( substring( CAST( @tmstamp AS CHAR ), 9, 6 ) AS TIMS ) AS time,
"Untyped literal
'ABAP' AS txt
WHERE fldate = datn`20240102`
INTO @DATA(misc_typed_literals).
out->write( data = misc_typed_literals name = `misc_typed_literals` ).
ENDMETHOD.
METHOD select_from_dbtab.
SELECT *
FROM zdemo_abap_carr
ORDER BY carrid
INTO TABLE @itab_res.
ENDMETHOD.
ENDCLASS.