"!

ABAP SQL
ABAP cheat sheet example class

"! "!

The example class demonstrates syntax and concepts related to ABAP SQL.
"! Choose F9 in ADT to run the class.

"! "!

Note

"!

Find information on getting started with the example class and the "! disclaimer in the ABAP Doc comment of class {@link zcl_demo_abap_aux}.

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.