diff --git a/03_ABAP_SQL.md b/03_ABAP_SQL.md index 3eac490..bc338ef 100644 --- a/03_ABAP_SQL.md +++ b/03_ABAP_SQL.md @@ -1105,7 +1105,8 @@ topic. | `... [NOT] LIKE ...` | The content of an operand matches (does not match) a specified pattern. The pattern can be specified by using wildcard characters. `%` stands for any character string, including an empty string. `_` stands for any character.| | `... IS [NOT] INITIAL ...` | The value of an operand is (not) the initial value of its built-in dictionary type.| | `... EXISTS ...` | Checks the result set of a [subquery](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensubquery_glosry.htm "Glossary Entry"). The expression is true if the result set contains at least one row. See more information [here](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwhere_logexp_subquery.htm).| -| `... [NOT] IN ( ... )` | Checks whether the operands on the left side match a value from a set of values specified in parentheses. On the left side, a single operand or an operand list are possible. On the right side, a comma-separated lists or subqueries can be specified.| +| `... [NOT] IN ...` | Checks whether the operands on the left side match a value from a set of values specified in parentheses. On the left side, a single operand or an operand list are possible. On the right side, a comma-separated lists or subqueries can be specified. It is also possible to specify a [ranges table](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenranges_table_glosry.htm) to evaluate [ranges conditions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenranges_condition_glosry.htm).| +| `... [NOT] NULL ...` | Checks whether the value of an operand is (not) the [null value](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abennull_value_glosry.htm). Find more information in the code snippet and in the [ABAP Keyword Documentation](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenwhere_logexp_null.htm). | > **💡 Note**
>You can combine multiple logical expressions into one @@ -1113,43 +1114,243 @@ logical expression using `AND` or `OR`. To further detail out the desired condition, expressions within parentheses are possible. -For demonstration purposes, various SQL conditions mentioned above are included in a `WHERE` clause: +Examples: ``` abap -SELECT * - FROM dbtab - WHERE comp1 = 'abc' "Equals some value - - "More example WHERE conditions: - AND comp2 > 100 "Greater than some value; alternatively GT is possible - - "Not equals plus an additional condition that must be respected - AND comp3 <> 100 AND comp4 = 'xyz' - - "(Not) between a value range - AND comp5 BETWEEN 1 AND 10 - AND comp6 NOT BETWEEN 1 AND 10 - - "A character literal has a certain pattern, preceded and - "followed by any string. - AND comp7 LIKE '%XYZ%' - - "The second character is not Y. _ stands for any character. - AND comp8 NOT LIKE '_Y%' - - "Contains one of the values specified in the parentheses - AND comp9 IN ( 'ABC', 'DEF', 'GHI' ) - - "Does not contain one of the values specified in the parentheses - AND comp10 NOT IN ( 'JKL', 'MNO' ) - - "Checking if an operand has an initial value - AND comp11 IS INITIAL - - "Combination of logical expression using AND, OR and parentheses - AND ( comp12 = a AND comp13 < b ) OR ( comp14 > c AND comp15 <> d ) - - INTO TABLE @DATA(itab_where). +"---- 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 TYPE SORTED TABLE OF demo_struc WITH UNIQUE KEY id. +"Populating internal table with data to work with in the examples +itab = 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 AS tab WHERE name = 'bear' INTO TABLE @DATA(it). "1 +SELECT id FROM @itab AS tab WHERE name <> 'bear' INTO TABLE @it. "2-12 +SELECT id FROM @itab AS tab WHERE id > 10 INTO TABLE @it. "11,12 +SELECT id FROM @itab AS tab WHERE id >= 10 INTO TABLE @it. "10,11,12 + +"---- Combining logical expressions using AND, OR and parentheses ---- +SELECT id FROM @itab AS tab WHERE id = 1 AND name = 'bear' INTO TABLE @it. "1 +SELECT id FROM @itab AS tab WHERE name = 'bear' OR name = 'sheep' INTO TABLE @it. "1,12 + +"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 AS tab + WHERE ( id = 1 AND name = 'bear' ) + AND ( id = 20 AND name = 'camel' ) + INTO TABLE @it. + +SELECT id FROM @itab AS tab + WHERE ( id = 1 AND name = 'bear' ) + OR ( id = 20 AND name = 'camel' ) + INTO TABLE @it. "1 + +"------------------------ [NOT] BETWEEN ------------------------ +SELECT id FROM @itab AS tab WHERE id BETWEEN 1 AND 4 INTO TABLE @it. "1,2,3,4 +"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 AS tab WHERE ( id >= 1 AND id <= 4 ) INTO TABLE @it. "1,2,3,4 +"Negation with NOT +SELECT id FROM @itab AS tab WHERE id NOT BETWEEN 1 AND 4 INTO TABLE @it. "5-12 + +"------------------------ IS [NOT] INITIAL ------------------------ +SELECT id FROM @itab AS tab WHERE id IS NOT INITIAL INTO TABLE @it. "1-12 + +"------------------------ [NOT] LIKE ------------------------ +"For (not) matching a specified pattern +"Note: % (any character string), _ (any character). +SELECT name FROM @itab AS tab + WHERE name LIKE '%ee%' + OR name LIKE '_o%' + INTO TABLE @DATA(names). "dog,deer,cheetah,donkey,sheep + +"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 = VALUE #( BASE itab ( id = 13 name = '100%' ) ). +"Any character sequence followed by the % character +SELECT name FROM @itab AS tab + WHERE name LIKE '%#%' ESCAPE '#' + INTO TABLE @names. "100% + +"Deleting the entry because it is not relevant for the further examples. +DELETE itab 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 AS tab + WHERE name IN ( 'camel', 'rabbit', 'dog', 'snake' ) + INTO TABLE @it. "2,3,5 + +"Negation NOT IN; note to use host variables/expressions for local/global data objects +DATA(animal) = 'sheep'. +SELECT id FROM @itab AS tab + WHERE name NOT IN ( 'fish', @animal ) + INTO TABLE @it. "1-10 + +"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 AS tab + WHERE ( id, name ) IN ( ( 1, 'bear' ), ( 3, 'rabbit' ), ( 8, 'zebra' ), ( 20, 'dog' ) ) + INTO TABLE @it. "1,3 + + +"------------------------ [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 AS tab + WHERE id IN ( SELECT key_field FROM zdemo_abap_tab1 ) INTO TABLE @it. "11,12 + +"------------------------ [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 AS tab WHERE id IN @rangestab INTO TABLE @it. "1,2,3,10,11,12 + + +"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 AS tab + WHERE name IN @rt + INTO TABLE @names. "dog,deer,cheetah,donkey,sheep + +"------------------------ 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 AS tab WHERE + EXISTS ( SELECT @abap_true FROM zdemo_abap_tab1 WHERE key_field = tab~id ) + INTO TABLE @it. "11,12 + +"------------------------ [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 visulaize 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). +*KEY_FIELD CHAR2 +*4 + +"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. +TYPES: BEGIN OF s2, + key_field TYPE zdemo_abap_tab2-key_field, + char2 TYPE zdemo_abap_tab1-char2, + END OF s2. +TYPES: 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. +*S2 NULLS +*KEY_FIELD CHAR2 KEY_FIELD CHAR2 +*1 y +*KEY_FIELD CHAR2 KEY_FIELD CHAR2 +*2 y +*KEY_FIELD CHAR2 KEY_FIELD CHAR2 +*3 z +*KEY_FIELD CHAR2 KEY_FIELD CHAR2 +*4 X + +"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. +*KEY_FIELD CHAR2 +*1 y +*2 y +*3 z ```

⬆️ back to top