原创 无峰 [ ABAP 技巧与实战 ](javascript:void\(0\);)
一
前言
CATALOG 或者SCHEMA 都属于数据库的抽象概念,主要用来解决命名冲突问题.
本文主要讨论怎么在ABAP的环境下访问数据库其它CATALOG中的表.
二
CATALOG VS SCHEMA
之前也没有认真理解这两个概念的差异, 只知道是架构在表的上级层次,(就像表家族的姓氏一般.) 以为二者只是不同数据库对同一个概念的不同称呼.
网上搜索了一下,有人认真比对了二者的差异. 因为HANA数据库的小众化,列表中没有提及.
但是从HANA STUDIO中的查看, HANA应该是支持catalog,不支持schema ,同时,每个用户都会有一个同名的catalog
三
ABAP环境的CATALOG名
ECC/S4系统安装的时候,也会在HANA数据库中创建一个CATALOG.
可以通过数据库工具(TCODE:DB02,还有其它TCODE也能进入相同的界面)查看,大部分的ECC/S4系统使用的HANA CATALOG名称是
SAPABAP1 或者SAPHANADB .
似乎安装系统的过程中,BASIS可以自定义CATALOG名称. ABAP数据字典对应的表都在这个CATALOG中.
大部分情况也不需要访问其它CATALOG的表.
四
访问其它CATALOG的情况
如果使用了SLT同步数据表内容 .
> 详见链接 无峰,公众号:ABAP 技巧与实战 [ SAP操作手册之 LT数据同步配置
> ](https://mp.weixin.qq.com/s?__biz=Mzg4MjA1MjE4NQ==&mid=2247485198&idx=1&sn=347f54003b85c13e91a8b30cdaad0b9d&chksm=cf5dda38f82a532eb20ab75c79ff58af8dd16e2ce2c0e69ce3fd1fab70e3b489722b90ea96b0&token=874183752&lang=zh_CN#rd)
SLT工具为了避免目标数据库存在同名表,会根据同步的配置名创建一个同名的CATALOG. 在目标数据库的ABAP系统中, 如果需要访问这些同步过来的表,
就需要涉及到本文讨论的话题了. 怎么访问数据库中其它CATALOG的表.
五
访问方式
• SQL编辑器访问
• OPENSQL 访问
• ADBC访问
• 外部视图访问
• CDS视图访问(ADMP)
_**01** _
**SQL编辑器访问**
SQL编辑器是ABAP中的一个SQL语句编辑执行的工具(通过DB02事务代码可以进入),可以方便的创建记录访问数据库的SQL语句及查看执行情况.
SQL编辑器的底层逻辑使用了ADBC访问访问数据库.
这种访问方式不是一种正式的访问方式. 推荐大家使用它来熟悉表结构及内容.
之前写过一个代码生成工具, 可以直接通过在SQL语句中添加注释,来生成一个ABAP报表程序(该文章是付费文章, 无法引用,感兴趣的请自行查找)
_**02** _
**NATIVE SQL 访问**
NATIVE SQL 是ABAP中支持的一种通过数据库SQL语句访问本地或外部数据库(通过DBCO建立连接) 的方式.
> DBCO建立数据库连接 无峰,公众号:ABAP 技巧与实战 [ ABAP基础知识 访问外部数据库-配置篇
> ](https://mp.weixin.qq.com/s?__biz=Mzg4MjA1MjE4NQ==&mid=2247486675&idx=1&sn=880947996ba5d9e851b666934c5d2f97&chksm=cf5dd1e5f82a58f3f4dfa774156a3853dcac4fc3950944ace75517dd689eceee2ca9aab19cb4&token=874183752&lang=zh_CN#rd)
> NATIVE SQL详见连接 无峰,公众号:ABAP 技巧与实战 [ ABAP基础知识 访问外部数据库-开发篇
> ](https://mp.weixin.qq.com/s?__biz=Mzg4MjA1MjE4NQ==&mid=2247486714&idx=1&sn=8ac7061472f7e0ac9e0a3f3356c8c8c5&chksm=cf5dd1ccf82a58da1a12329e5a9a3eef13dc9a3ddc3aecd5a1e22d9579ce43e4caff739fd89c&token=874183752&lang=zh_CN#rd)
>
_**03** _
**ADBC访问**
> ADBC 访问方式详见 无峰,公众号:ABAP 技巧与实战 [ ABAP基础知识 访问外部数据库-开发篇
> ](https://mp.weixin.qq.com/s?__biz=Mzg4MjA1MjE4NQ==&mid=2247486714&idx=1&sn=8ac7061472f7e0ac9e0a3f3356c8c8c5&chksm=cf5dd1ccf82a58da1a12329e5a9a3eef13dc9a3ddc3aecd5a1e22d9579ce43e4caff739fd89c&token=874183752&lang=zh_CN#rd)
数据同步平台也是采用的这种方式访问外部数据库
_**04** _
**外部视图访问**
先在HANA STUDIO 中创建HANA视图. (怎么创建HANA视图不在本文讨论范畴,有兴趣的可以自行百度). HANA视图的 default
client 属性中的session client 依赖于用户主记录中维护的session client . 建议session client 维护成
cross , 同时HANA视图中添加MANDT 字段. 在应用层面再确定CLIENT
用ECLIPS创建外部视图
外部视图可以在SE11/SE16N中访问. 同时可以像ABAP视图一样在ABAP语句中直接访问
_**05** _
**CDS视图访问(AMDP)**
AMDP(ABAP Managed Database Procedures)
AMDP是一种特殊的调用HANA 原生SQL的方式
CDS视图中是不支持直接使用HANA SQL语句, 只能如下方式实现.
创建一个类(该类只能在ECLIPSE中创建,SAPGUI中只能查看, 不能创建)和CDS视图配合来完成对数据库中其它CATALOG中的表的访问
2. 指定类方法关联 TABLE FUNCTION (CDS视图中的TABLE FUNCTION)
创建好的CDS视图因为不能定义ABAP视图名称, 因此无法通过SE11/SE16N访问, 只能通过ABAP语句访问
六
总结
本文的部分内容和前序访问外部数据库的方式有些重叠.
访问数据库的CATALOG也可以理解成访问一个指定的外部数据库(在创建DBCO连接时可以执行CATALOG的名称). 所以二者使用的方法是一致的.
最后两种(通过HANA视图创建外部视图访问,及通过CDS 及ADMP访问)就比较有针对性了. 更贴近本文的主题.
总之, 条条大路通罗马, 选择一个你最熟悉的路,或者在百度地图中标识最近的直通罗马. 咱们罗马见.
**约定**
如果你对这篇文章感兴趣,请帮忙点赞,在看,分享.
( **如果你真的喜欢这篇文章,请记得回来打个赏,作为支持我继续下去的动力,这是一个正反馈过程.
越多的人打赏,作者越有动力分享,读者就能享受更多的福利.毕竟打赏的金额富不了我,穷不了你,却能支持这个公众号长久发文.** )
**公众号 : syjf1976_abap**
**ABAP开发技巧**
**微信号 : 392077**
syjf1976
sharry_xlp
Yannick_Duan
申请进入公众号讨论群提问或者参与话题讨论
以下内容整理自 SAP 官方 ABAP 速查表(主题:ABAP SQL),作为本文的扩展补充资料。
which is the standardized language for accessing databases.
following:
| Keyword | Purpose |
|---|---|
SELECT | Reads data from data sources such as database tables and others |
INSERT | Adds rows to database tables |
UPDATE | Changes the content of rows of database tables |
MODIFY | Inserts rows into database tables or changes the content of existing rows |
DELETE | Deletes rows from database tables |
ABAP SQL. The considerations there are not relevant for this cheat sheet since the focus is on syntax options.
[!NOTE] - The syntax options for the SELECT statement are extensive. Make sure that you consult the ABAP Keyword Documentation for all available options. The cheat sheet and snippets demonstrate a selection. - The code examples in the cheat sheet primarily use DDIC database tables for CRUD operations. Note that there are also CDS artifacts that allow not only reading but also creating, updating, and deleting. See this section.
🟢 Click to expand for more details
This section provides bullet points on database tables and views which contain persisted data. Note that the code snippets in this cheat sheet focus on database tables as data source for ABAP SQL statements.
Database tables in AS ABAP ...
type. Plus, the definition of database tables consists of technical and semantic properties.
Find more information in the respective (sub)topics in the ABAP Keyword Documentation here.
Views ...
join with defined join conditions.
flat structure. The view's name can be used, for example, as data types to declare data objects, too.
purposes using SELECT.
"Classic" DDIC Views ...
"Modern" Views (since release 7.40)
views](https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/index.htm?file=abenexternal_view_glosry.htm "Glossary Entry") as proxies for SAP HANA views (attribute view, analytic view, calculation view)
defined using the SAP HANA Studio.
SAP HANA views "known" to the ABAP program. In doing so, the external views can be used like classic DDIC views as structured data types and as a source for reading operations with ABAP SQL.
CDS)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abencds_view_glosry.htm "Glossary Entry") ...
support annotations. Data sources can be combined using associations, views can be defined with input parameters, and more.
and used as a source for reading operations with ABAP SQL (using SELECT).
Language (DDL)](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddl_glosry.htm "Glossary Entry") in the ADT (that is, a source code editor, in contrast to a form-based editor).
systems (that support the ABAP CDS characteristics).
SELECT statements to retrieve data from one or more data sources (such as database tables, views or internal tables). Note that many syntax examples in this cheat sheet show a selection from dbtab denoting a database table as a source. SELECT statement includes several clauses that serve different purposes. WHERE clause should be specified to further restrict the read result.F1 for the keywords and additions to get all the details in the ABAP Keyword Documentation.SELECT FROM source "What data source to read from
FIELDS field_list "What columns should be read
WHERE condition "Specifies conditions on which a row/rows should be read
INTO target. "Specifies the target of the result setSELECT syntax (i.e. differently arranged or differently specified ABAP SQL syntax) that you should no longer use (strict syntax check modes enforce the use of specific ABAP SQL syntax) and/or are not possible in ABAP for Cloud Development. SELECT list, that can also be specified using the more modern FIELDS addition.F1 for the keywords and additions to get all the details in the ABAP Keyword Documentation.INTO clause should be placed after the other clauses. @ or @( ... ). Host variables represent data objects that are declared in ABAP programs. They are specified in an operand position of an ABAP SQL statement. Also see the SQL Operands section.SELECT list, i. e. the fields that are specified, can also be specified following the SELECT keyword before the FROM clause - without FIELDS. The following two SELECT statements are basically the same but differently arranged. The code snippets in the cheat sheet randomly use one syntax or the other. SELECT FROM dbtab
FIELDS comp1, comp2, comp3
...
SELECT comp1, comp2, comp3
FROM dbtab
...... INTO TABLE @DATA(itab)., to comfortably create an appropriate variable in place. Note that in case of internal tables as targets, the resulting table is a standard table and has an empty key which might have an impact when furtherprocessing the internal table entries. Find more information in the ABAP cheat sheet Internal Tables. The declaration operator FINAL can be used to declare immutable variables.
SELECT list or FIELDS clause, you define the structure of the result set.* to read all columns.SINGLE after SELECT. With SINGLE, it means the result set is a single row result set. Otherwise, it is a multirow result set, or a SELECT loop is opened. An appropriate data object must be specified in the INTO clause.| Syntax | Notes |
|---|---|
SELECT <em> ... SELECT ... FIELDS </em> ... | As outlined above, the * character defines all columns to be read from a data source (in the order specified there). |
SELECT col1, col2, col3 ... SELECT ... FIELDS col1, col2, col3 ... | A comma-separated list of individual column names. |
SELECT data_source~col1, data_source~col2, data_source~col3 ... SELECT ... FIELDS data_source~col1, data_source~col2, data_source~col3 ... | A comma-separated list of individual column names. Here, the name of the data source is explicitly specified and precedes the column name, separated by a tilde. |
SELECT data_source~<em> ... SELECT ... FIELDS data_source~</em> ... | In this case, the name of the data source is followed by a tilde and the * character to specify all columns. Note that there are special conditions when using this variant. |
SELECT col1 AS al1, col2 AS al2, col3 AS al3 ... SELECT ... FIELDS col1 AS al1, col2 AS al2, col3 AS al3 ... | Defining alias names for individual columns of the result set with AS; make sure that you use an alias name only once here. In the statement, the alias name can only be used after an ORDER BY clause; as shown further down, in some cases (e. g. when using SQL expressions) the specification of an alias name is required; setting an alias name for the data source is also possible (SELECT FROM dbtab AS alias_name ...). See the section on joins further down. |
[!NOTE] - You have plenty of options regarding the specification of the columns in the SELECT list, among them, the outlined direct specification of the column name. SQL expressions can be specified, too. See more details here and in the sections on SQL expressions further down.
"All fields
SELECT * FROM dbtab
WHERE ...
INTO ...
"Comma-separated list
SELECT col1, col2, col3
FROM dbtab
WHERE ...
INTO ...
"Comma-separated list, data source explicitly specified
SELECT dbtab~col1, dbtab~col2, col3
FROM dbtab
WHERE ...
INTO ...
"Data source explicitly specified, all fields
SELECT dbtab~*
FROM dbtab
WHERE ...
INTO ...
"Alias names
"Consider the following: You want to read data from a database table into a target data
"object but, for example, a name in the target is different. Provided that there will
"not be an issue regarding the type (conversion) when the values are assigned, you might
"specify an alias name for the database column to match a component's name in the target data object.
SELECT comp1 AS comp_a, comp2 AS comp_b, comp3 AS comp_c
FROM dbtab
WHERE ...
INTO CORRESPONDING FIELDS OF TABLE @itab.
"Alias name also possible for the data source
SELECT ds~col1, ds~col2, ds~col3
FROM dbtab AS ds
WHERE ...
INTO ...Example code using a demo database table from the cheat sheet repository, and using the FIELDS addition.
"The examples do not specify a WHERE clause.
"All fields
SELECT FROM zdemo_abap_carr
FIELDS *
INTO TABLE @DATA(itab).
"Comma-separated list
SELECT FROM zdemo_abap_carr
FIELDS carrid, carrname, url
INTO TABLE @itab.
"Comma-separated list, data source explicitly specified
SELECT FROM zdemo_abap_carr
FIELDS zdemo_abap_carr~carrid, zdemo_abap_carr~carrname, url
INTO TABLE @itab.
"Data source explicitly specified, all fields
SELECT FROM zdemo_abap_carr
FIELDS zdemo_abap_carr~*
INTO TABLE @itab.
"Alias names
SELECT FROM zdemo_abap_carr
FIELDS carrid AS comp_a, carrname AS comp_b, url AS comp_c
INTO TABLE @DATA(itab_comp_alias).
"Alias name also possible for the data source
SELECT FROM zdemo_abap_carr AS ds
FIELDS ds~carrid, ds~carrname, ds~url
INTO TABLE @DATA(itab_db_alias).FROM clause specifies data sources from which data is read. SELECT statements with CDS view entities as data sources. @, and an alias name must be specified. SELECT *
FROM dbtab
WHERE ...
INTO ....
SELECT FROM @itab1 AS tab
FIELDS ...
WHERE ...
INTO ....
SELECT *
FROM cds_view
WHERE ...
INTO ....Example using cheat sheet repository objects, a demo internal table, and the FIELDS addition:
"Database table
SELECT FROM zdemo_abap_fli
FIELDS *
INTO TABLE @DATA(itab_db).
"CDS entities
"CDS view entity
SELECT *
FROM zdemo_abap_fli_ve
INTO TABLE @DATA(itab_ve).
"CDS table function
SELECT FROM zdemo_abap_table_function
FIELDS *
INTO TABLE @DATA(itab_tf).
"Internal table
TYPES: BEGIN OF s,
num TYPE i,
chars TYPE c LENGTH 3,
END OF s,
t_type TYPE TABLE OF s WITH EMPTY KEY.
DATA(itab) = VALUE t_type( ( num = 1 chars = 'aaa' )
( num = 2 chars = 'bbb' )
( num = 3 chars = 'ccc' ) ).
SELECT FROM @itab AS it
FIELDS *
INTO TABLE @DATA(itab_it).This section demonstrates a selection of ABAP SQL SELECT statement patterns for specifying target-related additions to handle query results. There is a wide range of options available, some of which have already been covered above.
Syntax options regarding the target:
| Syntax | Notes |
|
|
An internal table is expected for the result set. It may be declared inline to automatically have a suitable type, which also applies to |
|
|
Expects a structure when used without |
|
|
|
|
|
The addition |
NEW addition: Specifying an anonymous data object as target object |
|
|
|
Apart from reading into structures and internal tables outlined above, you can also read into individual elementary data objects. Here, the individual elementary data objects as target objects are specified in a comma-separated list (e. g. as existing host variables or declared inline) and put between a pair of parentheses. Note:
|
More target-related additions:
| Subject | Details/Code Snippet |
|
|
|
| Retrieving multiple rows into an internal table |
|
SELECT loop: Sequentially passing multiple rows |
|
| Checking the existence of a row in a database table |
|
DISTINCT addition: Removing rows that occur more than once in a multirow result set |
|
UP TO n ROWS addition: Limiting the number of returned table rows |
|
OFFSET n addition: Returning only the table rows after a row with a specified count from the result set |
|
PACKAGE SIZE n addition: Storing the result in packages of a specified number of rows |
The addition |
INDICATORS [NOT] NULL STRUCTURE addition: Specifying null indicators |
|
| Clause | Details/Code Snippet |
GROUP BY |
Note that the In the example below, the database table rows that have the same content in column |
HAVING |
|
ORDER BY |
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 ordering options, for example, by using SQL expressions. Find more information here. [!NOTE] - Not specifying |
WHERE |
|
FOR ALL ENTRIES addition:
WHERE clause in logical expressions for comparisons with a column of the data source.SELECT statement is the union set of the result sets produced by the individual evaluations. Rows that occur more than once are removed from the result set automatically. The entire content of a row is respected.FOR ALL ENTRIES is specified, there must be at least one comparison with a column of the internal table.SELECT statement, for example, in WHERE clauses or joins, instead of using the FOR ALL ENTRIES addition."Checking that table is not initial
IF ( 0 < lines( itab2 ) ).
SELECT comp1, comp2, comp3
FROM dbtab
FOR ALL ENTRIES IN @itab2 "Host variable before internal table
WHERE comp1 = @itab2-comp1 ... "Relational expression on the right side of a comparison
INTO TABLE @itab1
ENDIF.Checking the result set of a subquery with the addition EXISTS
See possible clauses and additions of a subquery in a condition in ABAP SQL here.
The following code snippet includes a parenthesized subquery following EXISTS. Data is only selected from dbtab1 if the relational expression (WHERE EXISTS ...) is true, i. e. if the result set of the subquery contains at least one row. Note the components of the table that are referenced using a tilde.
SELECT comp1, comp2, comp3
FROM dbtab1 AS tab1
WHERE EXISTS
( SELECT comp1 FROM dbtab2
WHERE comp1 = tab1~comp1 AND comp2 = tab1~comp2 )
INTO ...| Subject | Details/Code Snippet |
| Using an inner join |
selector](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentable_comp_selector_glosry.htm "Glossary Entry") |
| Using an outer join |
|
| Merging the result sets of multiple queries into a single result set |
... using the set operator |
| Returning distinct rows of a result set (1) |
... of a query specified before the |
| Returning distinct rows of a result set (2) |
... of a query specified before the |
[!NOTE] - There are more join variants and syntax options available. See the ABAP Keyword Documentation on joins
for more information.
- There a more syntax options and contexts forUNION,INTERSECT, andEXCEPT. Find more information here. -MERGEstatements allow modification operations on a data source by evaluating another data source. Find more information here.
When to use Common Table Expressions (CTE):
SELECTstatement and especially if you need them more than once.
SELECT FROM subquery), which is not possible in ABAP SQL.How they work:
WITH introduces the definition of CTEs.
as data source; CTEs can be considered as temporary views, which only exist for the duration of the database access.
query](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenmainquery_glosry.htm "Glossary Entry"), i. e. a SELECT statement accesses the result of the expressions.
Setup of a statement with CTE:
WITH+ characterresult set, within parentheses
AS followed by a subquery with SELECT whichcreates the tabular result set of the CTE
SELECT in which the previousCTEs are to be used as data source
SELECT loop is opened and data is written into a workarea in the closing main query, the loop must be closed with ENDWITH. (which fulfills the same task as ENDSELECT.).
[!NOTE] - Each CTE must be used at least once, either in another CTE or in the
main query. The main query must access at least one CTE.
- The result set of a CTE never has a [client
column](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclient_column_glosry.htm "Glossary Entry").
- See more information in this topic
and further options and additions when using CTEs in the subtopics.
Example: 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 stored in the internal 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(result).You can formulate conditions in ABAP SQL statements, i. e. logical expressions, especially in the WHERE clause to restrict the result. Note that without a WHERE clause, all rows are respected for the operation.
Find more details and examples in the WHERE Conditions cheat sheet.
Among the syntax options are:
... a = b ... / ... a EQ b ... ... a <> b ... / ... a NE b ... ... a < b ... / ... a LT b ...... a > b ... / ... a GT b ... ... a <= b ... / ... a LE b ... ... a >= b ... / ... a GE b ...... AND ... / ... OR ... / ... ( ... ) ...... a [=|<>|>|<|...] [ALL|ANY|SOME] ( SELECT ... ) ...... a [NOT] BETWEEN b AND c ...... a [NOT] LIKE b [ESCAPE c] ...... a IS [NOT] INITIAL ...... a [NOT] IN (b, c, ...)...... a [NOT] IN ( SELECT ... ) ...... ( a, b, ... ) IN ( ( d, e, ... ) ( f, g, ...) ... ) ...... ( a, b, ... ) IN ( SELECT ... ) ...... a [NOT] IN @ranges_table ...... EXISTS ( SELECT ... ) ...... a IS [NOT] NULL ...... (dynamic_where_clause) ... operands](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abensql_operand_glosry.htm "Glossary Entry") are elementary operands in ABAP SQL statements
literal, host variables (i. e. global or local data objects escaped using @: @dobj) or host expressions (@( ... ))
character @. The literals can be typed (using the type name and content within a pair of backquotes: char\) with built-in ABAP Dictionary types or untyped. See the Typed Literals section further down.abc\
possible as host expressions for statements modifying the content of database tables as shown further down.
here.
[!NOTE] Questions about when to use what, what is possible in which contexts and positions, is beyond the scope of this cheat sheet. Check the details in the
respective topics in the ABAP Keyword Documentation. Find a general overview of important operand positions in ABAP SQL here. Due to the rich variety of options, the cheat sheet covers a selection.
Example demonstrating possible operands:
DATA number TYPE i VALUE 3.
SELECT FROM zdemo_abap_flsch
FIELDS
"Specifies a column of a data source directly using its name
cityfrom,
"Column selector ~ can be used to prefix every specified column.
"Here, it is optional. It is non-optional, e. g., if multiple data
"sources in an ABAP SQL statement are edited and the column name
"is not unique.
zdemo_abap_flsch~cityto,
'Lufthansa' AS name, "Untyped literal
char`X` AS flag, "Typed literal
@number AS num, "Host variable
@( cl_abap_context_info=>get_system_date( ) ) as date "Host expression
WHERE carrid = 'LH' "Untyped literal
AND countryfr = char`DE` "Typed literal
"Data object created inline and escaped with @
INTO TABLE @DATA(it)
"The following clause shows all options having the same effect
UP TO 3 ROWS. "Untyped numeric literal
"UP TO int4`3` ROWS. "Typed numeric literal
"UP TO @number ROWS. "Host variable
"UP TO @( 10 - 7 ) ROWS. "Host expressionSELECT list as demonstrated in most of the following examples.
on SQL expressions here and the subtopics there.
[!NOTE] You can enclose SQL expressions in parentheses. ``abap SELECT SINGLE carrid, "SQL expressions enclosed in parenthesis in the context "of arithmetic expressions impacting the prioriry of "calculations ( 1 + 2 ) <em> ( 3 + 4 ) </em> ( 5 + 6 ) AS calc_w_par, "231 1 + 2 <em> 3 + 4 </em> 5 + 6 AS calc_no_par "33 FROM zdemo_abap_fli WHERE carrid = 'AA' INTO @DATA(using_parentheses). ``
operands above: A value from the data source (i.e. the column name), values from an ABAP program passed to the database (literal, host variable or host expression).
SELECT list in the example above.here.
Arithmetic expressions perform arithmetic calculations using the operators +, -, *, /.
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).SELECT SINGLE
carrid,
"A cast expression converts the value of the operands to the
"specified dictionary type. The result is a representation of the
"source value in the specified type.
CAST( 1 AS D34N ) / CAST( 2 AS D34N ) AS ratio,
CAST( connid AS INT4 ) AS connidnum,
CAST( @( cl_abap_context_info=>get_system_date( ) ) AS CHAR ) AS dat
FROM zdemo_abap_fli
WHERE carrid = 'AA'
INTO @DATA(cast_expr).String expressions use the operator && to concatenate character strings.
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).ELSE means that the result is the null value. The null value can be specified explicitly by the null expression NULL.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).and aggregate the values of multiple rows of the result set of a query into a single value
here.
Example:
"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).How window expressions work:
"window") of a database query that implements ABAP SQL
which evaluates the rows of the window and which can, for example, be an aggregate function like AVG to determine the average value - to the result set
which all the window functions have the same result; a value is then determined for the rows of a window
Setup of a statement with window expressions:
AVG,followed by OVER( ... ) (the content in the parentheses defines the "window")
PARTITION BY: Defines the windows using acomma-separated list of SQL expressions; the window function is calculated for the rows of this window; note that if the addition is not specified, the window comprises all rows of the result set
ORDER BY: Introduces both an order (you canuse ASCENDING and DESCENDING) and a frame (as outlined below) within the current window, which further restricts the rows for which the window function is calculated
window, can optionally be defined if ORDER BY is specified; there are 3 options to define the starting and ending frame boundaries (see the example)
See more information on window expressions and the syntax here.
Examples:
"Example 1: A simple window is constructed in the OVER clause;
"window functions - here aggregate functions - are applied
SELECT carrid, currency,
SUM( paymentsum ) OVER( PARTITION BY carrid ) AS sum,
AVG( price AS DEC( 14,2 ) ) OVER( PARTITION BY carrid ) AS avg,
MAX( price ) OVER( PARTITION BY carrid ) AS max
FROM zdemo_abap_fli
ORDER BY carrid
INTO TABLE @DATA(win).
"Example 2:
SELECT carrid, currency, fldate,
"Sorts the rows by some columns and counts the number of rows from
"the first row of the window to the current row.
COUNT( * ) OVER( ORDER BY currency, fldate
ROWS BETWEEN
"UNBOUNDED PRECEDING: frame starts at the first row of the window
UNBOUNDED PRECEDING
"CURRENT ROW: determines starting or ending at the current row; here, it ends
AND CURRENT ROW ) AS count1,
"If no window frame is used, the default window frame is
"BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,
"i. e. the result of count1 equals the result of count2.
COUNT( * ) OVER( ORDER BY currency, fldate ) AS count2,
"Sorts the rows by some columns and counts the number of rows from
"the current row to the last row of the window.
"The result is reverse numbering.
COUNT( * ) OVER( ORDER BY currency, fldate
ROWS BETWEEN CURRENT ROW
UNBOUND FOLLOWING:
"Determines the ending frame boundary, this addition specifies the last row of the window
AND UNBOUNDED FOLLOWING ) AS count_reverse,
"Sorts the rows by some columns and calculates the rolling averages
"of a subset of rows from column price. The subset consists of the
"current row plus one preceding and one following row. Another use
"case as below example that uses prices would be that, for example,
"you can calculate the 3-day-average temperature for every day from
"a list of temperature data.
AVG( price AS DEC( 14,2 ) ) OVER( ORDER BY currency, fldate
ROWS BETWEEN
"n PRECEDING: for both start and end of frame; frame to start/end n rows above the current row
1 PRECEDING
"n FOLLOWING: for both start and end of frame; frame to start/end n rows beneath the current row
AND 1 FOLLOWING ) AS avg
FROM zdemo_abap_fli
INTO TABLE @DATA(result).The null value is represented by the NULL operand in ABAP SQL statements. Find more information here.
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).SELECT SINGLE
carrname,
"Division, result rounded to an integer
"Result: 2
div( 4, 2 ) AS div,
"Division, 3rd argument: result is rounded to the specified
"number of decimals
"Result: 0.33
division( 1, 3, 2 ) AS division,
"Result is rounded to first greater integer
"Result: 2
ceil( decfloat34`1.333` ) AS ceil,
"Result is the remainder of division
"Result: 1
mod( 3, 2 ) AS mod,
"Result: Largest integer value not greater than the specified value
"Result: 1
floor( decfloat34`1.333` ) AS floor,
"Returns the absolute number
"Result: 2
abs( int4`-2` ) AS abs,
"Result is rounded to the specified position after the decimal separator
"Result: 1.34
round( decfloat34`1.337`, 2 ) AS round
FROM zdemo_abap_carr
WHERE carrid = 'AA'
INTO @DATA(numeric_functions).SELECT SINGLE
carrid, "LH
carrname, "Lufthansa
url, "http://www.lufthansa.com
"Concatenates strings, ignores trailing blanks
"Result: LHLufthansa
concat( carrid, carrname ) AS concat,
"Concatenates strings, number denotes the blanks that are inserted
"Result: LH Lufthansa
concat_with_space( carrid, carrname, 1 ) AS concat_with_space,
"First letter of a word -> uppercase, all other letters -> lowercase;
"note that a space and other special characters means a new word.
"Result: Http://Www.Lufthansa.Com
initcap( url ) AS initcap,
"Position of the first occurrence of the substring specified
"Result: 6
instr( carrname,'a' ) AS instr,
"String of length n starting from the left of an expression;
"trailing blanks are ignored
"Result: Luft
left( carrname, 4 ) AS left,
"Number of characters in an expression, trailing blanks are ignored
"Result: 24
length( url ) AS length,
"Checks if expression contains a PCRE expression;
"case-sensitive by default (case_sensitive parameter can be specified)
"Notes on the result: 1 = found, 0 = not found
"Result: 1
like_regexpr( pcre = '\..', "Period that is followed by any character
value = url ) AS like_regex,
"Returns position of a substring in an expression,
"3rd parameter = specifies offset (optional)
"4th parameter = determines the number of occurrences (optional)
"Result: 9
locate( carrname, 'a', 0, 2 ) AS locate,
"Searches a PCRE pattern, returns offset of match;
"many optional parameters: occurrence, case_sensitive, start, group
"Result: 21
locate_regexpr( pcre = '\..', "Period followed by any character
value = url,
occurrence = 2 ) "2nd occurrence in the string
AS locate_regexpr,
"Searches a PCRE pattern, returns offset of match + 1;
"many optional parameters: occurrence, case_sensitive, start, group
"Result: 2
locate_regexpr_after( pcre = '.', "Any character
value = url,
occurrence = 1 ) AS locate_regexpr_after,
"Removes leading characters as specified in the 2nd argument,
"trailing blanks are removed
"Result: ufthansa
ltrim( carrname, 'L' ) AS ltrim,
"Counts all occurrences of found PCRE patterns
"Result: 2
occurrences_regexpr( pcre = '\..', "Period that is followed by any character
value = url ) AS occ_regex,
"Replaces the 2nd argument with the 3rd in an expression
"Result: Lufth#ns#
replace( carrname, 'a', '#' ) AS replace,
"Replaces a found PCRE expression;
"more parameters possible: occurrence, case_sensitive, start
"Result: http://www#ufthansa#om
replace_regexpr( pcre = '\..', "Period that is followed by any character
value = url,
with = '#' ) AS replace_regex,
"Extracts a string with the length specified starting from the right
"Result: hansa
right( carrname, 5 ) AS right,
"Expands string to length n (2nd argument); trailing blanks produced
"are replaced by the characters from the (3rd) argument
"Note that if n is less than the string, the expression is truncated
"on the right.
"Result: Lufthansa###
rpad( carrname, 12, '#' ) AS rpad,
"All trailing characters that match the character of the 2nd argument
"are removed; trailing blanks are removed, too
"Result: Lufthans
rtrim( carrname, 'a' ) AS rtrim,
"Returns a substring; 2nd argument = position from where to start;
"3rd argument: length of the extracted substring
"Result: fth
substring( carrname, 3, 3 ) AS substring,
"Searches for a PCRE expression and returns the matched substring
"More parameters possible: occurrence, case_sensitive, start, group
"Result: .lu
substring_regexpr( pcre = '\...', "Period that is followed by any two characters
value = url ) AS substring_regexpr,
"All lower case letters are transformed to upper case letters
"Result: LUFTHANSA
upper( carrname ) AS upper
FROM zdemo_abap_carr
WHERE carrid = 'LH'
INTO @DATA(string_functions)."The null value is a special value that is returned by a database. It indicates an
"undefined value or result. Note that, in ABAP, there are no special null values. Do
"not confuse the null value with a type-dependent initial value. When using SELECT
"statements to read data, null values can be produced by, for example, outer joins.
"When the null values are passed to a data object, they are transformed to the
"type-dependent initial values. For more information, refer to the ABAP Keyword Documentation.
"The following example uses a left outer join to intentionally create null values. For
"this purpose, two demo database tables of the ABAP cheat sheet repository are cleared and
"populated with specific values to visualize null values.
DELETE FROM zdemo_abap_tab1.
DELETE FROM zdemo_abap_tab2.
MODIFY zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 1 char1 = 'a' char2 = 'y' )
( key_field = 2 char1 = 'b' char2 = 'z' ) ) ).
MODIFY zdemo_abap_tab2 FROM TABLE @( VALUE #( ( key_field = 1 char1 = 'a' )
( key_field = 2 char1 = 'a' )
( key_field = 3 char1 = 'b' )
( key_field = 4 ) ) ).
"Note that for the entry 'key_field = 4' no char1 value was passed.
"char1 is a shared column of the two database tables, and which is used in
"the ON condition of the join. Since there is no entry in char1 for 'key_field = 4',
"the joined values are null in that case.
"The coalesce function is used to replace null values produced by an outer join with
"a different value.
SELECT tab2~key_field,
coalesce( tab1~char1, '-' ) AS coalesced1,
coalesce( tab1~char2, '#' ) AS coalesced2,
"A coalesce function is a short form of a complex
"case distinction such as the following:
CASE WHEN tab1~char1 IS NOT NULL THEN tab1~char1
ELSE '?'
END as coalesced3
FROM zdemo_abap_tab2 AS tab2
LEFT OUTER JOIN zdemo_abap_tab1 AS tab1 ON tab1~char1 = tab2~char1
INTO TABLE @DATA(join_w_null).
*Example table content
*KEY_FIELD COALESCED1 COALESCED2 COALESCED3
*1 a y a
*2 a y a
*3 b z b
*4 - # ?| Function | Notes | Code Snippet |
|
|
Converts byte strings (type |
The code snippet implements the following:
|
|
|
Converts character strings (type |
The code snippet implements the following:
|
|
|
Converts from a byte field (type |
The code snippet implements the following:
|
|
|
|
The example class, executable with F9 in ADT, implements the following:
|
|
|
Converts units for a value passed to the |
The code snippet implements the following:
|
|
|
|
The code snippet implements the following:
|
|
|
Converts geometry input in the Extended Well-Known Binary (EWKB) representation to a geometry object in JSON format |
|
Find more information in the ABAP Keyword Documentation. The Built-In Functions cheat sheet covers code examples.
SELECT SINGLE
carrid,
"Selection of time and date-related functions
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
INTO @DATA(tab_w_date_time_func).SELECT SINGLE
carrid,
"UUID
uuid( ) AS uuid
FROM zdemo_abap_carr
INTO @DATA(tab_w_uuid).[!NOTE] - The following sections include code patterns. To explore various syntax options with an executable example, see section Example: Exploring ABAP SQL Statements Changing Data in Database Tables below. - There are also CDS artifacts that allow not only reading but also creating, updating, and deleting. See this section.
@) or hostexpressions (with @( ... )).
sy-subrc (0 = single row or all rows inserted successfully, 4 = row not or not all rows inserted) and sy-dbcnt (number of rows that are inserted) are set.INSERT"Inserting a single row into a database table
INSERT dbtab FROM @row.
"Alternative syntax, same effect
INSERT INTO dbtab VALUES @row.
"Line is created inline using the VALUE operator as part of a host expression
INSERT dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ).
"Inserting multiple lines from an internal table into a database table.
"Make sure that the internal table does not contain a line having the same key
"as an existing row in the database table. Otherwise, a runtime error occurs.
INSERT dbtab FROM TABLE @itab.
"Inserting lines from a table declared inline using the VALUE operator
"as part of a host expression
INSERT dbtab FROM TABLE @( VALUE #( ( comp1 = ... comp2 = ... )
( comp1 = ... comp2 = ... ) ) ).
"ACCEPTING DUPLICATE KEYS addition: To avoid the runtime error mentioned above,
"all lines that would produce duplicate entries in the database table
"regarding the keys are discarded and sy-subrc is set to 4.
INSERT dbtab FROM TABLE @itab ACCEPTING DUPLICATE KEYS.
"Inserting the result set of an embedded subquery
"Here, multiple result sets can be joined, e. g. using UNION.
INSERT dbtab FROM ( SELECT ... ).INSERT, sy-subrc and sy-dbcnt are set.UPDATE"Changing content by overwriting entire rows based on a structure
UPDATE dbtab FROM @row.
UPDATE dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ). "Using a host expression
"Changing content by overwriting entire rows based on rows in an internal table
UPDATE dbtab FROM TABLE @itab.
"Using a host expression
UPDATE dbtab FROM TABLE @( VALUE #( ( comp1 = ... comp2 = ... )
( comp1 = ... comp2 = ... ) ) ).
*&---------------------------------------------------------------------*
*& SET addition
*&---------------------------------------------------------------------*
"Changing values of specific fields without overwriting other, non-specified
"fields
"Changing values of specific fields in all table rows
"There are mutliple options for the value assignment. E.g. you can use
"a literal, host variable/expression, SQL function, and so on.
"The following statement does not use a WHERE clause. Therefore,
"the comp2 values of all database table entries are changed.
UPDATE dbtab SET comp2 = 'X'.
"Changing values of specific fields in all found entries by restricting
"the data sets to be changed using a WHERE clause
"Use a comma-separated list for the components after SET
UPDATE dbtab SET comp2 = 'X', comp3 = 'Y' WHERE comp4 > 100.
"Changing values of specific fields in a single database table entry
"Assume the entry can be uniquely identified by specifying key values
"in the WHERE clause
UPDATE dbtab SET comp2 = 'X' WHERE key_field = 'Y'.
*&---------------------------------------------------------------------*
*& INDICATORS ... SET STRUCTURE addition
*&---------------------------------------------------------------------*
"Similar to SET, using the INDICATORS ... addition, you can change content
"of specific fields without overwriting existing values of other fields by
"specifying set indicators.
"Example:
"- Structured type is created with WITH INDICATORS addition
"- Internal table from which to update dbtab 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)
TYPES ind_wa TYPE dbtab WITH INDICATORS comp_ind TYPE abap_bool.
DATA ind_tab TYPE TABLE OF ind_wa.
ind_tab = VALUE #(
( comp1 = ... comp2 = ... comp_ind-comp2 = abap_true )
( comp1 = ... comp2 = ... comp_ind-comp2 = abap_true ) ).
UPDATE dbtab FROM TABLE @ind_tab INDICATORS SET STRUCTURE comp_ind.
"In the following example, the logic is reversed using NOT.
UPDATE dbtab FROM TABLE @ind_tab INDICATORS NOT SET STRUCTURE comp_ind.sy-subrc and sy-dbcnt are set.MODIFY"Inserting a single row into a database table or changing an existing row
MODIFY dbtab FROM @row.
"Using a host expression
MODIFY dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ).
"Inserting/Changing multiple rows
MODIFY dbtab FROM TABLE @itab.
"Using a host expression
MODIFY dbtab FROM TABLE @( VALUE #( ( comp1 = ... comp2 = ... )
( comp1 = ... comp2 = ... ) ) ).
"Inserting/Changing multiple rows based on a result set of an embedded subquery
MODIFY dbtab FROM ( SELECT ... ).sy-subrc and sy-dbcnt are set.DELETE"Variant DELETE FROM ...: Either all rows are deleted or restricted
"All rows are deleted
DELETE FROM dbtab.
"Rows are deleted based on a condition
DELETE FROM dbtab WHERE ....
"Note that there are further options available, e. g. ORDER BY, UP TO
"Variant DELETE ... FROM ...: Deleting a single row or multiple row
DELETE dbtab FROM @row.
"Using a host expression
DELETE dbtab FROM @( VALUE #( comp1 = ... ) ).
"Rows are deleted based on data in an internal table
DELETE dbtab FROM TABLE @itab.
"Using a host expression
DELETE dbtab FROM TABLE @( VALUE #( ( comp1 = ... )
( comp1 = ... ) ) ).Constructor expressions can be very handy in ABAP SQL statements. For more information about constructor expressions, see the ABAP Keyword Documentation and the Constructor Expressions cheat sheet. Many additions are available. The following examples show a selection. The previous code snippets already include the use of the VALUE operator with which structures and internal tables can be created in place.
*&---------------------------------------------------------------------*
*& VALUE
*&---------------------------------------------------------------------*
"VALUE operator as shown above, creating an internal table in place
INSERT dbtab FROM TABLE @( VALUE #( ( key_field = 1 comp1 = ... )
( key_field = 2 comp1 = ... )
( key_field = 3 comp1 = ... ) ) ).
"Inserting a table row from a row created in place
INSERT dbtab FROM @( VALUE #( key_field = 4 comp1 = ... ) ).
"FOR LOOP with VALUE
DATA(it) = VALUE some_it_type( ( key_field = 5 comp1 = ... )
( key_field = 6 comp1 = ... )
( key_field = 7 comp1 = ... ) ).
"In the following example, the internal table from above is looped across. You
"can imagine modifying the components (or not) when mapping the fields (as indicated
"by the concatentation in the example). In doing so, the internal table values may or
"may be not modified and inserted into the database table.
INSERT dbtab FROM TABLE @( VALUE #( FOR wa IN it ( key_field = wa-key_field
comp1 = wa-comp1 && 'XYZ'
... ) ) ).
"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.
SELECT SINGLE * FROM dbtab WHERE key_field = ... INTO @DATA(read_line).
"Assumed comp1 or comp3 components are not specified, but they retain their
"original content and are not initialized when writing to the database table.
UPDATE dbtab FROM @( VALUE #( BASE read_line comp2 = ... comp4 = ... ) ).
*&---------------------------------------------------------------------*
*& CORRESPONDING
*&---------------------------------------------------------------------*
"The following example assumes that some_itab has a different line type than dbtab.
"I.e. some_itab may have more components that are not available in dbtab. The
"corresponding fields with identical names are used. It is assumed that the components'
"types are compatible or convertible.
INSERT dbtab FROM TABLE @( CORRESPONDING #( some_itab ) ).
"This example assumes that field names are not identical. Using the CORRESPONDING operator
"and its additions, you can carry out a mapping and, for example, exclude components. It is
"assumed that the components' types are compatible or convertible.
INSERT dbtab FROM TABLE @( CORRESPONDING #( another_itab MAPPING key_field = key comp1 = compZ EXCEPT comp2 ) ).To try the example out, create a demo class named zcl_demo_abap. If it already exists, reuse it. Otherwise, create a new class with a different name. Paste the code into it. If you choose a different class name, update the class name in the code snippet accordingly. After activation, choose F9 in ADT to execute the class. The example uses a database table of the ABAP cheat sheets repository and is set up to display output in the console.
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
*&---------------------------------------------------------------------*
*& INSERT
*&---------------------------------------------------------------------*
"Deleting the contents of a demo database table to start with an empty database table
DELETE FROM zdemo_abap_tab1.
"Inserting a single row into a database table
DATA(row_a) = VALUE zdemo_abap_tab1( key_field = 1 char1 = 'aaa' char2 = 'bbb' num1 = 10 num2 = 100 ).
INSERT zdemo_abap_tab1 FROM @row_a.
"Alternative syntax, same effect
DATA(row_b) = VALUE zdemo_abap_tab1( key_field = 2 char1 = 'ccc' char2 = 'ddd' num1 = 20 num2 = 200 ).
INSERT INTO zdemo_abap_tab1 VALUES @row_b.
"Line is created inline using the VALUE operator as part of a host expression
INSERT zdemo_abap_tab1 FROM @( VALUE #( key_field = 3 char1 = 'eee' char2 = 'fff' num1 = 30 num2 = 300 ) ).
"Inserting multiple lines from an internal table into a database table.
"Make sure that the internal table does not contain a line having the same key
"as an existing row in the database table. Otherwise, a runtime error occurs.
TYPES it_type TYPE TABLE OF zdemo_abap_tab1 WITH EMPTY KEY.
DATA(it_a) = VALUE it_type( ( key_field = 4 char1 = 'ggg' char2 = 'hhh' num1 = 40 num2 = 400 )
( key_field = 5 char1 = 'iii' char2 = 'jjj' num1 = 50 num2 = 500 ) ).
INSERT zdemo_abap_tab1 FROM TABLE @it_a.
"Inserting lines from a table declared inline using the VALUE operator
"as part of a host expression
INSERT zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 6 char1 = 'kkk' char2 = 'lll' num1 = 60 num2 = 600 )
( key_field = 7 char1 = 'mmm' char2 = 'nnn' num1 = 70 num2 = 700 ) ) ).
"ACCEPTING DUPLICATE KEYS addition: To avoid the runtime error mentioned above,
"all lines that would produce duplicate entries in the database table
"regarding the keys are discarded and sy-subrc is set to 4.
DATA(it_b) = VALUE it_type( ( key_field = 1 char1 = '###' char2 = '###' num1 = 0 num2 = 0 )
( key_field = 8 char1 = 'ooo' char2 = 'ppp' num1 = 80 num2 = 800 ) ).
INSERT zdemo_abap_tab1 FROM TABLE @it_b ACCEPTING DUPLICATE KEYS.
ASSERT sy-subrc = 4.
"Inserting the result set of an embedded subquery
"Various options are available. The examples show a selection.
"The subqueries use an internal table.
"No restriction specified, all entries are inserted
DATA(it_c) = VALUE it_type( ( key_field = 9 char1 = 'qqq' char2 = 'rrr' num1 = 90 num2 = 900 )
( key_field = 10 char1 = 'sss' char2 = 'ttt' num1 = 100 num2 = 1000 )
( key_field = 11 char1 = 'uuu' char2 = 'vvv' num1 = 110 num2 = 1100 ) ).
INSERT zdemo_abap_tab1 FROM ( SELECT key_field, char1, char2, num1, num2 FROM @it_c AS itc ).
"WHERE condition specified
DATA(it_d) = VALUE it_type( ( key_field = 12 char1 = 'www' char2 = 'xxx' num1 = 120 num2 = 1200 )
( key_field = 13 char1 = 'yyy' char2 = 'zzz' num1 = 130 num2 = 1300 )
( key_field = 14 char1 = 'AAA' char2 = 'BBB' num1 = 140 num2 = 1400 ) ).
INSERT zdemo_abap_tab1 FROM ( SELECT key_field, char1, char2, num1, num2 FROM @it_d AS itd WHERE key_field <= 13 ).
"Using a subquery and replacing existing values in a CASE expression
DATA(it_e) = VALUE it_type( ( key_field = 15 char1 = 'X' char2 = 'DDD' num1 = 150 num2 = 1500 )
( key_field = 16 char1 = 'Y' char2 = 'FFF' num1 = 160 num2 = 1600 )
( key_field = 17 char1 = 'Z' char2 = 'HHH' num1 = 170 num2 = 1700 ) ).
INSERT zdemo_abap_tab1 FROM (
SELECT key_field,
CASE WHEN char1 = 'X' THEN 'CCC'
WHEN char1 = 'Y' THEN 'EEE'
ELSE 'GGG'
END AS char1,
char2, num1, num2
FROM @it_e AS ite ).
"Retrieving all database entries for display purposes
SELECT * FROM zdemo_abap_tab1 INTO TABLE @DATA(itab_insert).
out->write( data = itab_insert name = `itab_insert` ).
*&---------------------------------------------------------------------*
*& UPDATE
*&---------------------------------------------------------------------*
"Preparing a demo database table
DELETE FROM zdemo_abap_tab1.
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 )
( key_field = 3 char1 = 'eee' char2 = 'fff' num1 = 30 num2 = 300 ) ) ).
"Changing content by overwriting entire rows based on a structure
DATA(row_c) = VALUE zdemo_abap_tab1( key_field = 1 char1 = 'ggg' char2 = 'hhh' num1 = 12 num2 = 123 ).
UPDATE zdemo_abap_tab1 FROM @row_c.
"The following example specifies a value for the key field that does not
"exist in the database table. Consequently, no update takes place, the sy-subrc
"value is set to 4.
UPDATE zdemo_abap_tab1 FROM @( VALUE #( key_field = 4 char1 = 'iii' char2 = 'jjj' num1 = 44 num2 = 456 ) ).
ASSERT sy-subrc = 4.
"Changing content by overwriting entire rows based on rows in an internal table
DATA(it_j) = VALUE it_type( ( key_field = 2 char1 = 'kkk' char2 = 'lll' num1 = 23 num2 = 234 )
( key_field = 3 char1 = 'mmm' char2 = 'nnn' num1 = 34 num2 = 345 ) ).
UPDATE zdemo_abap_tab1 FROM TABLE @it_j.
"Using a host expression, internal table created in place
INSERT zdemo_abap_tab1 FROM @( VALUE #( key_field = 4 char1 = 'ooo' char2 = 'ppp' num1 = 40 num2 = 400 ) ).
"The following example does not specify two components. Initial values are used.
UPDATE zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 4 char2 = 'qqq' num1 = 44 ) ) ).
"INDICATORS addition: Changing content of specific fields without overwriting
"existing values of other fields
TYPES ind_wa TYPE zdemo_abap_tab1 WITH INDICATORS comp_ind TYPE abap_boolean.
TYPES ind_tab TYPE TABLE OF ind_wa WITH EMPTY KEY.
INSERT zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 5 char1 = 'rrr' char2 = 'sss' num1 = 50 num2 = 500 )
( key_field = 6 char1 = 'ttt' char2 = 'uuu' num1 = 60 num2 = 600 ) ) ).
UPDATE zdemo_abap_tab1 FROM TABLE @( VALUE ind_tab( ( key_field = 5 char1 = 'vvv' char2 = 'www' num1 = 56 num2 = 567
comp_ind-char1 = abap_true comp_ind-char2 = abap_false
comp_ind-num1 = abap_true comp_ind-num2 = abap_false )
( key_field = 6 char1 = 'xxx' char2 = 'yyy' num1 = 67 num2 = 678
comp_ind-char1 = abap_false comp_ind-char2 = abap_true
comp_ind-num1 = abap_false comp_ind-num2 = abap_true ) ) )
INDICATORS SET STRUCTURE comp_ind.
SELECT * FROM zdemo_abap_tab1 INTO TABLE @DATA(itab_update).
out->write( data = itab_update name = `itab_update` ).
"SET addition: Changing values of specific fields in all table rows
"Preparing a demo database table
DELETE FROM zdemo_abap_tab1.
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 )
( key_field = 3 char1 = 'eee' char2 = 'fff' num1 = 30 num2 = 300 ) ) ).
"The following example transforms the character string of a
"component to upper case. Since no WHERE clause is specified,
"the char1 components of all database table entries are affected
UPDATE zdemo_abap_tab1 SET char1 = upper( char1 ).
"Changing values of specific fields in all found entries by restricting
"the data sets to be changed using a WHERE clause
UPDATE zdemo_abap_tab1 SET char2 = concat( char2, '#' ), num1 = num1 + 1, num2 = num2 + 2 WHERE num1 > 15.
"Changing values of specific fields in a single database table entry
"assuming the entry can be uniquely identified by specifying key values
"in the WHERE clause
"Use a comma-separated list after SET to specify multiple components
INSERT zdemo_abap_tab1 FROM @( VALUE #( key_field = 99 char1 = 'A' char2 = 'B' num1 = 99 num2 = 100 ) ).
UPDATE zdemo_abap_tab1 SET char2 = 'X', num1 = 1, num2 = 2 WHERE key_field = 99.
SELECT * FROM zdemo_abap_tab1 INTO TABLE @DATA(itab_update_set).
out->write( data = itab_update_set name = `itab_update_set` ).
*&---------------------------------------------------------------------*
*& MODIFY
*&---------------------------------------------------------------------*
"The examples include INSERT statements to prepare the database table.
"Deleting the contents of a demo database table to start with an empty database table
DELETE FROM zdemo_abap_tab1.
"Inserting a single row into a database table
DATA(row_d) = VALUE zdemo_abap_tab1( key_field = 1 char1 = 'aaa' char2 = 'bbb' num1 = 10 num2 = 100 ).
MODIFY zdemo_abap_tab1 FROM @row_d.
"Inserting a table row using a host expression and a row created in place
MODIFY zdemo_abap_tab1 FROM @( VALUE #( key_field = 2 char1 = 'ccc' char2 = 'ddd' num1 = 20 num2 = 200 ) ).
"Inserting a table row ...
MODIFY zdemo_abap_tab1 FROM @( VALUE #( key_field = 3 char1 = 'eee' char2 = 'fff' num1 = 30 num2 = 300 ) ).
"... and modifying it. No new row is inserted, the existing one is modified as the key already exists.
MODIFY zdemo_abap_tab1 FROM @( VALUE #( key_field = 3 char1 = 'ggg' char2 = 'hhh' num1 = 34 num2 = 345 ) ).
"Inserting table rows from an internal table
MODIFY zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 4 char1 = 'iii' char2 = 'jjj' num1 = 40 num2 = 400 )
( key_field = 5 char1 = 'kkk' char2 = 'lll' num1 = 50 num2 = 500 ) ) ).
"Modifying/inserting from an internal table
MODIFY zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 4 char1 = 'mmm' char2 = 'nnn' num1 = 45 num2 = 456 )
( key_field = 5 char1 = 'ooo' char2 = 'ppp' num1 = 56 num2 = 567 )
( key_field = 6 char1 = 'qqq' char2 = 'rrr' num1 = 60 num2 = 600 )
( key_field = 7 char1 = 'sss' char2 = 'ttt' num1 = 70 num2 = 700 ) ) ).
SELECT * FROM zdemo_abap_tab1 INTO TABLE @DATA(itab_modify).
out->write( data = itab_modify name = `itab_modify` ).
*&---------------------------------------------------------------------*
*& DELETE
*&---------------------------------------------------------------------*
"Deleting the contents of a demo database table to start with an empty database table
DELETE FROM zdemo_abap_tab1.
SELECT * FROM zdemo_abap_tab1 INTO TABLE @DATA(itab_delete).
ASSERT itab_delete IS INITIAL.
"Inserting demo data into the database table
INSERT zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 1 num1 = 10 )
( key_field = 2 num1 = 20 )
( key_field = 3 num1 = 30 )
( key_field = 4 num1 = 40 )
( key_field = 5 num1 = 50 )
( key_field = 6 num1 = 60 )
( key_field = 7 num1 = 70 )
( key_field = 8 num1 = 80 ) ) ).
"Rows are deleted based on a condition
DELETE FROM zdemo_abap_tab1 WHERE key_field >= 7.
"Deleting a single row based on entries in a structure
"Keys are specified
DELETE zdemo_abap_tab1 FROM @( VALUE #( key_field = 1 ) ).
"There is no entry with key_field = 0.
DELETE zdemo_abap_tab1 FROM @( VALUE zdemo_abap_tab1( num1 = 20 ) ).
ASSERT sy-subrc = 4.
"Deleting multiple rows based on entries in an internal table
DELETE zdemo_abap_tab1 FROM TABLE @( VALUE #( ( key_field = 2 )
( key_field = 3 ) ) ).
SELECT * FROM zdemo_abap_tab1 INTO TABLE @itab_delete.
out->write( data = itab_delete name = `itab_delete` ).
*&-----------------------------------------------------------------------*
*& Exploring constructor expressions for internal tables created in place
*&-----------------------------------------------------------------------*
"For more information about constructor expressions, see the ABAP Keyword Documentation and the
"Constructor Expressions cheat sheet. Many additions are available. The examples show a
"selection.
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( data = itab_constr name = `itab_constr` ).
ENDMETHOD.
ENDCLASS.The following code snippet shows a selection of syntax options for ABAP SQL MERGE statements:
MERGE INTO target [AS target_alias]
USING { source [AS source_alias] | @itab AS itab_alias [DECLARE CLIENT clnt] } ON join_condition
WHEN MATCHED [AND match_condition1 [AND match_condition2 ... ] ] THEN { UPDATE { ALL | SET set_condition } | DELETE }
WHEN NOT MATCHED [AND match_condition [AND match_condition2 ... ]] THEN INSERT { ALL | ( columns_list ) VALUES ( values_expressions ) } ... .MERGE allows modification operations on a data source by evaluating another data source.MERGE statements perform a right outer join.USING clause:@ to escape the table, and an alias is mandatory. If available, you can specify the client field for an internal table using DECLARE CLIENT.ON are those of SELECT statements.WHEN MATCHED clause, a WHEN NOT MATCHED clause, or both. If both are used, the WHEN MATCHED clause must come before the WHEN NOT MATCHED clause.WHEN MATCHED clause:AND.UPDATE: Updates the target with source content based on match conditions. Using ALL means all source fields are considered for the update. SET allows specifying conditions for conditional updates. Conditions can be represented by expressions, such as ABAP SQL functions.DELETE: Deletes datasets from the target when matching datasets are found in the source.WHEN NOT MATCHED clause:AND and involving both source and target components. ALL means all unmatched datasets in the source are inserted into the target table. The number of columns must be the same for both the target and source.VALUES addition allows you to specify concrete columns and values to insert. VALUES is preceded by a parenthesized, comma-separated list of column names, which can be a single column name and can only be specified once. VALUES is followed by a parenthesized, comma-separated list of values that correspond to the specified columns. The number of values must match the number of specified column names.Expand the following collapsible section for example code. To try the example out, create a demo class named zcl_demo_abap. If it already exists, reuse it. Otherwise, create a new class with a different name. Paste the code into it. If you choose a different class name, update the class name in the code snippet accordingly. After activation, choose F9 in ADT to execute the class. The example is set up to display output in the console.
🟢 Click to expand for example code
CLASS zcl_demo_abap DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
METHODS del.
METHODS prep_db.
METHODS prep_it.
METHODS display_dbtab_content IMPORTING out TYPE REF TO if_oo_adt_classrun_out.
METHODS set_example_divider IMPORTING out TYPE REF TO if_oo_adt_classrun_out
text TYPE string.
DATA itabdb TYPE TABLE OF zdemo_abap_tab1 WITH EMPTY KEY.
TYPES: BEGIN OF s1,
comp_a TYPE i,
comp_b TYPE c LENGTH 10,
comp_c TYPE c LENGTH 10,
comp_d TYPE i,
comp_e TYPE i,
END OF s1.
DATA it TYPE TABLE OF s1 WITH EMPTY KEY.
METHODS:
m01_alias_names IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m02_join_conditions IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m03_wm_extra_condition IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m04_using_itab IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m05_wm_then_update_all IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m06_wm_then_delete IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m07_wnm_then_insert_all IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m08_wm_and_wnm_then_insert_all IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m09_wnm_then_insert_col_list IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m10_coalesce IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m11_dynamic_merge IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string,
m12_system_fields IMPORTING out TYPE REF TO if_oo_adt_classrun_out text TYPE string.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_abap IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
"Dynamically calling methods of the class
"The method names are retrieved using RTTI. For more information, refer to the
"Dynamic Programming ABAP cheat sheet.
"Only those methods should be called that follow the naming convention M + digit.
DATA(methods) = CAST cl_abap_classdescr( cl_abap_typedescr=>describe_by_object_ref( me ) )->methods.
SORT methods BY name ASCENDING.
"To call a particular method only, you can comment in the WHERE clause and
"adapt the literal appropriately.
LOOP AT methods INTO DATA(meth_wa)
"WHERE name CS 'M01'
.
TRY.
"The find function result indicates that the method name begins (offset = 0) with M and a digit.
IF find( val = meth_wa-name pcre = `^M\d` case = abap_false ) = 0.
CALL METHOD (meth_wa-name) EXPORTING out = out text = CONV string( meth_wa-name ).
ENDIF.
CATCH cx_root INTO DATA(error).
out->write( error->get_text( ) ).
ENDTRY.
ENDLOOP.
ENDMETHOD.
METHOD prep_db.
del( ).
INSERT zdemo_abap_tab1 FROM TABLE @(
VALUE #(
( key_field = 1
char1 = 'A1'
char2 = 'A2'
num1 = 1
num2 = 2 )
( key_field = 2
char1 = 'B1'
char2 = 'B2'
num1 = 3
num2 = 4 )
( key_field = 3
char1 = 'C1'
char2 = 'C2'
num1 = 5
num2 = 6 )
( key_field = 4
char1 = 'D1'
char2 = 'D2'
num1 = 7
num2 = 8 )
( key_field = 9
char1 = 'E1'
char2 = 'E2'
num1 = 9
num2 = 10 )
( key_field = 10
char1 = 'F1'
char2 = 'F2'
num1 = 11
num2 = 12 ) ) ).
INSERT zdemo_abap_tab2 FROM TABLE @(
VALUE #(
( key_field = 1
char1 = 'Z'
num1 = 100
numlong = 200 )
( key_field = 2
char1 = 'Y'
num1 = 300
numlong = 400 )
( key_field = 3
char1 = 'X'
num1 = 500
numlong = 600 )
( key_field = 4
char1 = 'W'
num1 = 700
numlong = 800 )
( key_field = 6
char1 = 'V'
num1 = 900
numlong = 1000 )
( key_field = 7
char1 = 'W'
num1 = 1100
numlong = 1200 ) ) ).
ENDMETHOD.
METHOD del.
DELETE FROM zdemo_abap_tab1.
DELETE FROM zdemo_abap_tab2.
ENDMETHOD.
METHOD display_dbtab_content.
SELECT * FROM zdemo_abap_tab1 ORDER BY key_field INTO TABLE @itabdb.
out->write( itabdb ).
ENDMETHOD.
METHOD prep_it.
CLEAR it.
it = VALUE #(
( comp_a = 1
comp_b = 'IT_1'
comp_c = 'IT_2'
comp_d = 100
comp_e = 99 )
( comp_a = 2
comp_b = 'IT_3'
comp_c = 'IT_4'
comp_d = 98
comp_e = 97 )
( comp_a = 3
comp_b = 'IT_5'
comp_c = 'IT_6'
comp_d = 96
comp_e = 95 )
( comp_a = 4
comp_b = 'IT_7'
comp_c = 'IT_8'
comp_d = 94
comp_e = 93 )
( comp_a = 5
comp_b = 'IT_9'
comp_c = 'IT_10'
comp_d = 92
comp_e = 91 )
( comp_a = 6
comp_b = 'IT_11'
comp_c = 'IT_12'
comp_d = 90
comp_e = 89 ) ).
ENDMETHOD.
METHOD m01_alias_names.
set_example_divider( out = out text = |{ text }: (Not) Using alias names| ).
"The following example statements use and do not use alias names for the source
"and target.
"The statements use the WHEN MATCHED clause and THEN UPDATE SET.
"WHEN MATCHED THEN UPDATE SET allows specifying set conditions to perform
"conditional update operations. The follwing examples illustrate that the
"values specified for the conditions can be represented by expressions, for
"example, using ABAP SQL functions.
prep_db( ).
"Without alias names
MERGE INTO zdemo_abap_tab1
USING zdemo_abap_tab2
ON zdemo_abap_tab1~key_field = zdemo_abap_tab2~key_field
WHEN MATCHED THEN UPDATE SET char1 = '#'.
display_dbtab_content( out ).
out->write( |\n| ).
prep_db( ).
"With alias names
MERGE INTO zdemo_abap_tab1 AS a
USING zdemo_abap_tab2 AS b
ON a~key_field = b~key_field
WHEN MATCHED THEN UPDATE SET char1 = '#'.
display_dbtab_content( out ).
ENDMETHOD.
METHOD m02_join_conditions.
set_example_divider( out = out text = |{ text }: Join condition options| ).
"Most of the statements in this example use fairly simple join conditions using
"comparisons per field values. This is to emphasize that more complex conditions
"such as using AND, OR, and parenthesized expressions are possible.
prep_db( ).
MERGE INTO zdemo_abap_tab1 AS a
USING zdemo_abap_tab2 AS b
ON ( a~key_field = b~key_field AND b~numlong = 400 )
OR ( a~key_field = b~key_field AND b~num1 > 600 )
WHEN MATCHED THEN UPDATE SET char1 = '#'.
display_dbtab_content( out ).
ENDMETHOD.
METHOD m03_wm_extra_condition.
set_example_divider( out = out text = |{ text }: WHEN MATCHED with extra condition| ).
"After WHEN MATCHED, AND can optionally be used to specify extra conditions.
"Multiple AND additions followed by condition specifications can be used.
"The WHEN NOT MATCHED clause can also be specified with extra conditions.
prep_db( ).
MERGE INTO zdemo_abap_tab1 AS a
USING zdemo_abap_tab2 AS b
ON a~key_field = b~key_field
WHEN MATCHED AND b~num1 <> 100
THEN UPDATE SET char1 = '#'.
display_dbtab_content( out ).
ENDMETHOD.
METHOD m04_using_itab.
set_example_divider( out = out text = |{ text }: USING with internal table| ).
"In the example MERGE statements here, the INTO clause uses a database table as
"target. You can also use other data sources such as CDS table entities.
"In the USING clause, internal tables can be specified.
prep_db( ).
prep_it( ).
MERGE INTO zdemo_abap_tab1 AS a
USING @it AS b
ON a~key_field = b~comp_a
WHEN MATCHED THEN UPDATE SET char1 = '#'.
display_dbtab_content( out ).
ENDMETHOD.
METHOD m05_wm_then_update_all.
set_example_divider( out = out text = |{ text }: WHEN MATCHED THEN UPDATE ALL| ).
prep_db( ).
prep_it( ).
"Note:
"- The number of columns of the target table and values must be equal.
" Therefore, the first statement does not work. The database tables
" have a different number of columns.
"- The column names in the target and source table do not have to match,
" but the columns' types must be compatible.
* MERGE INTO zdemo_abap_tab1 AS a
* USING zdemo_abap_tab2 AS b
* ON a~key_field = b~key_field
* WHEN MATCHED THEN UPDATE ALL.
MERGE INTO zdemo_abap_tab1 AS a
USING @it AS b
ON a~key_field = b~comp_a
WHEN MATCHED THEN UPDATE ALL.
display_dbtab_content( out ).
ENDMETHOD.
METHOD m06_wm_then_delete.
set_example_divider( out = out text = |{ text }: WHEN MATCHED THEN DELETE| ).
prep_db( ).
MERGE INTO zdemo_abap_tab1 AS a
USING zdemo_abap_tab2 AS b
ON a~key_field = b~key_field
WHEN MATCHED THEN DELETE.
display_dbtab_content( out ).
ENDMETHOD.
METHOD m07_wnm_then_insert_all.
set_example_divider( out = out text = |{ text }: WHEN NOT MATCHED THEN INSERT ALL| ).
prep_db( ).
prep_it( ).
"Note:
"- The number of columns of the target table and values must be equal.
"- The example statement uses the WHEN NOT MATCHED clause only.
MERGE INTO zdemo_abap_tab1 AS a
USING @it AS b
ON a~key_field = b~comp_a
WHEN NOT MATCHED THEN INSERT ALL.
display_dbtab_content( out ).
out->write( |\n| ).
prep_db( ).
"The following example emphasizes that joins, depending on the join
"conditions, can result in duplicate entries.
it = VALUE #( ( comp_a = 1
comp_b = 'X'
comp_c = 'Y'
comp_d = 100
comp_e = 99 ) ).
TRY.
MERGE INTO zdemo_abap_tab1 AS a
USING @it AS b
ON a~char1 = b~comp_b
WHEN NOT MATCHED THEN INSERT ALL.
display_dbtab_content( out ).
CATCH cx_sy_open_sql_db INTO DATA(e).
out->write( e->get_text( ) ).
ENDTRY.
ENDMETHOD.
METHOD m08_wm_and_wnm_then_insert_all.
set_example_divider( out = out text = |{ text }: WHEN NOT MATCHED THEN INSERT ALL, with WHEN MATCHED clause| ).
"Note:
"- The WHEN MATCHED clause always precedes the WHEN NOT MATCHED clause.
"- The example reflects an upsert, i.e. when matched, entries are updated,
" otherwise, an insertion occurs.
"- Specifying both clauses is optional.
prep_db( ).
prep_it( ).
MERGE INTO zdemo_abap_tab1 AS a
USING @it AS b
ON a~key_field = b~comp_a
WHEN MATCHED THEN UPDATE SET char1 = '#'
WHEN NOT MATCHED THEN INSERT ALL.
display_dbtab_content( out ).
ENDMETHOD.
METHOD m09_wnm_then_insert_col_list.
set_example_divider( out = out text = |{ text }: WHEN NOT MATCHED THEN INSERT columns_list| ).
prep_db( ).
prep_it( ).
MERGE INTO zdemo_abap_tab1 AS a
USING zdemo_abap_tab2 AS b
ON a~key_field = b~key_field
WHEN NOT MATCHED
THEN INSERT ( key_field, char1, char2 ) VALUES ( b~key_field, 'AB', 'AP' ).
display_dbtab_content( out ).
ENDMETHOD.
METHOD m10_coalesce.
set_example_divider( out = out text = |{ text }: coalesce| ).
"The following example illustrates that for certain column specifications
"and join conditions, null values can be produced. If detected by the
"compiler, a syntax warning occurs. In such cases, the coalesce function
"can be specified.
prep_db( ).
MERGE INTO zdemo_abap_tab1 AS a
USING zdemo_abap_tab2 AS b
ON a~key_field = b~key_field
WHEN NOT MATCHED
THEN INSERT ( key_field, char1 ) VALUES ( b~key_field, coalesce( b~char1, 'ABAP' ) ).
display_dbtab_content( out ).
ENDMETHOD.
METHOD m11_dynamic_merge.
set_example_divider( out = out text = |{ text }: Dynamic MERGE statements | ).
prep_db( ).
DATA(dyn_merge) = `INTO zdemo_abap_tab1 USING zdemo_abap_tab2 ` &&
`ON zdemo_abap_tab1~key_field = zdemo_abap_tab2~key_field ` &&
`WHEN MATCHED THEN UPDATE SET char1 = '#'`.
MERGE (dyn_merge).
display_dbtab_content( out ).
out->write( |\n| ).
prep_db( ).
"Erroneous content for dynamic merge statement
"In the example statement, INTO is missing.
DATA(dyn_merge_error) = `zdemo_abap_tab1 USING zdemo_abap_tab2 ` &&
`ON zdemo_abap_tab1~key_field = zdemo_abap_tab2~key_field ` &&
`WHEN MATCHED THEN UPDATE SET char1 = '?'`.
TRY.
MERGE (dyn_merge_error).
display_dbtab_content( out ).
CATCH cx_sy_dynamic_osql_syntax INTO DATA(err).
out->write( err->get_text( ) ).
ENDTRY.
out->write( |\n| ).
out->write( |\n| ).
prep_db( ).
"The following statement illustrates a dynamic MERGE statetement
"using a string table.
DATA(dyn_merge_tab) = VALUE string_table(
( `INTO zdemo_abap_tab1 USING zdemo_abap_tab2` )
( `ON zdemo_abap_tab1~key_field = zdemo_abap_tab2~key_field` )
( `WHEN MATCHED THEN UPDATE SET char1 = '*'` ) ).
MERGE (dyn_merge_tab).
display_dbtab_content( out ).
ENDMETHOD.
METHOD m12_system_fields.
set_example_divider( out = out text = |{ text }: System field setting| ).
"sy-subrc: 0 if at least one entry in the target was updated, inserted
" or deleted; otherwise, the value is 4.
"sy-dbcnt: The sum of all entries updated, inserted and/or deleted in
" the target.
prep_db( ).
prep_it( ).
MERGE INTO zdemo_abap_tab1 AS a
USING @it AS b
ON a~key_field = b~comp_a
WHEN MATCHED THEN UPDATE SET char1 = '#'
WHEN NOT MATCHED THEN INSERT ALL.
DATA(sysubrc) = sy-subrc.
DATA(sydbcnt) = sy-dbcnt.
out->write( |sy-subrc: { sysubrc }| ).
out->write( |sy-dbcnt: { sydbcnt }| ).
out->write( |\n| ).
out->write( |\n| ).
display_dbtab_content( out ).
out->write( |\n| ).
out->write( |\n| ).
prep_db( ).
it = VALUE #( ( comp_a = 123
comp_b = 'IT_A'
comp_c = 'IT_B'
comp_d = 456
comp_e = 789 ) ).
MERGE INTO zdemo_abap_tab1 AS a
USING @it AS b
ON a~key_field = b~comp_a
WHEN MATCHED THEN UPDATE SET char1 = '#'.
sysubrc = sy-subrc.
sydbcnt = sy-dbcnt.
out->write( |sy-subrc: { sysubrc }| ).
out->write( |sy-dbcnt: { sydbcnt }| ).
out->write( |\n| ).
out->write( |\n| ).
display_dbtab_content( out ).
out->write( |\n| ).
out->write( |\n| ).
prep_db( ).
it = VALUE #( ( comp_a = 1 )
( comp_a = 2 )
( comp_a = 123
comp_b = 'IT_A'
comp_c = 'IT_B'
comp_d = 100
comp_e = 200 )
( comp_a = 456
comp_b = 'IT_C'
comp_c = 'IT_D'
comp_d = 300
comp_e = 400 ) ).
MERGE INTO zdemo_abap_tab1 AS a
USING @it AS b
ON a~key_field = b~comp_a
WHEN MATCHED THEN DELETE
WHEN NOT MATCHED THEN INSERT ALL.
sysubrc = sy-subrc.
sydbcnt = sy-dbcnt.
out->write( |sy-subrc: { sysubrc }| ).
out->write( |sy-dbcnt: { sydbcnt }| ).
out->write( |\n| ).
out->write( |\n| ).
display_dbtab_content( out ).
ENDMETHOD.
METHOD set_example_divider.
out->write( |\n| ).
out->write( |*&{ repeat( val = `-` occ = strlen( text ) + 2 ) }*| ).
out->write( |*& { text }| ).
out->write( |*&{ repeat( val = `-` occ = strlen( text ) + 2 ) }*| ).
out->write( |\n| ).
out->write( |\n| ).
ENDMETHOD.
ENDCLASS.DATA(dbtab) = 'ZDEMO_ABAP_FLSCH'.
"Selecting from a dynamically specified database table.
SELECT *
FROM (dbtab)
WHERE ...
INTO ...define table entity ....define writable view entity ....Example
🟢 Click to expand for example code
zdemo_abap_animals_te for the table entity, and zdemo_abap_animals_we for the writable CDS view entity), or use other names and adapt the code accordingly.SELECT, INSERT, MODIFY, UPDATE, and DELETE statements.| ABAP repository object | Notes/Code |
|
Table entity |
|
|
Writable CDS view entity |
|
|
Class |
|
As mentioned in several sections above, ABAP system fields such as sy-subrc and sy-dbcnt are set in the context of ABAP SQL statements, which can be evaluated, e.g. using a subsequent statement IF sy-subrc = .... The following table shows a selection of values.
| Statement | sy-subrc | sy-dbcnt |
|---|---|---|
SELECT | 0: Values were passed successfully to a target data object 4: Result set is empty. Typically, this means that there are no entries found (matching conditions specified). | The number of rows that were passed. |
INSERT | 0: Single row (work area specified) or all rows (internal table specified) inserted 4: Row not or not all rows inserted (in case of multiple rows to be inserted, ACCEPTING DUPLICATE KEYS was specified) | The number of rows that were inserted. |
MODIFY | 0: Single row (work area specified) or all rows (internal table specified) inserted or modified 4: Row not or not all rows inserted or modified | The number of rows that were processed. |
UPDATE | 0: Single row (work area specified) or all rows updated 4: Row not or not all rows updated | The number of rows that were updated. |
DELETE | DELETE FROM target variant: 0: At least one row deleted (with WHERE clause specified), all or n rows deleted (without WHERE clause specified) 4: No row deleted DELETE target FROM variant: 0: Row deleted (work area specified), all rows deleted (internal table specified) 4: Row not deleted (work area specified), not all specified rows deleted (internal table specified) | The number of rows that were deleted. |
The following example explores the setting of sy-subrc and sy-dbcnt by ABAP SQL statements using a demo database table from the cheat sheet repository.
"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(itab).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 5.
ASSERT sy-dbcnt = lines( itab ).
SELECT *
FROM zdemo_abap_tab1
WHERE key_field <= 3
INTO TABLE @DATA(itab2).
ASSERT sy-subrc = 0.
ASSERT sy-dbcnt = 3.
SELECT *
FROM zdemo_abap_tab1
WHERE key_field > 10
INTO TABLE @DATA(itab3).
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(itab4).
ASSERT sy-subrc = 4.
ASSERT sy-dbcnt = 0."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).[!IMPORTANT] - ABAP SQL features implicit client handling. - While you can disable this in classic ABAP, it is not an option in ABAP Cloud, where you are limited to accessing your own client. - Unlike ABAP SQL, there is no implicit client handling in Native SQL. AMDP, which uses Native SQL, is usable in ABAP Cloud, but it is crucial to ensure that AMDP only accesses client-safe repository objects, meaning it only accesses data from your own client. For this purpose, dedicated additions are provided.
Notes on ABAP SQL:
"Using the current client implicitly
SELECT * FROM zdemo_abap_carr INTO TABLE @DATA(itab1).
"For example, it is not possible to specify the client explicitly in a WHERE clause.
"The following statement shows a syntax error.
"SELECT * FROM zdemo_abap_carr WHERE mandt = '123' INTO TABLE @DATA(itab2).USING CLIENT (F1 docu for Standard ABAP) addition that modifies this default behavior. It disables implicit client handling.CLIENT SPECIFIED addition. It is obsolete, only USING CLIENT should be used. "---- The following code is for classic ABAP only. ----
"SQL SELECT statements turning off the implicit client handling, and
"specifying different clients to retrieve data from
"Literal containing a client ID
SELECT * FROM zdemo_abap_carr USING CLIENT '000' INTO TABLE @DATA(itab3).
"Data object containing a client ID (must be of type c length 3)
"Note: If the client does not exist, the result set is empty.
TYPES c3 LENGTH 3.
DATA client_id TYPE c3 VALUE '000'.
SELECT * FROM zdemo_abap_carr USING CLIENT @client_id INTO TABLE @DATA(itab4).
"Selecting from multiple clients
"Using a ranges table
DATA client_range TYPE RANGE OF c3.
client_range = VALUE #( ( sign = 'I' option = 'EQ' low = '000' )
( sign = 'I' option = 'BT' low = '100' high = '200' ) ).
SELECT * FROM zdemo_abap_carr USING CLIENTS IN @client_range INTO TABLE @DATA(itab5).
"Selecting data of all clients whose client IDs are in the column MANDT of the
"system table T000
SELECT * FROM zdemo_abap_carr USING CLIENTS IN t000 INTO TABLE @DATA(itab6).
"Selecting data of all clients regardless of the content of the client column
SELECT * FROM zdemo_abap_carr USING ALL CLIENTS INTO TABLE @DATA(itab7).Notes on Native SQL/AMDP
USING list must be client-safe, including CDS table functions implemented as AMDP methods.Notes on CDS view entities
There are RAP-specific variants of ABAP SQL statements that use the MAPPING FROM ENTITY addition. Find more information here and in the ABAP for RAP: Entity Manipulation Language (ABAP EML) cheat sheet.
SELECT statements. Find more information and examples in the ABAP Keyword Documentation. For working with hierarchies, see the ABAP SQL: Working with Hierarchies cheat sheet.[!NOTE] - The executable example covers the following topics, among others: - Reading from database tables usingSELECT- Various additions toSELECTstatements - Changing data in database tables usingINSERT,UPDATE,MODIFYandDELETE- Excursions: Operands and expressions in ABAP SQL statements - The steps to import and run the code are outlined here. - Disclaimer
03_ABAP_SQL.md。版权归原作者所有。