ABAP Cookbook
Content
- Working with variables
- Working with references
- Working with internal tables
- Working with references and field-symbols
- Working with objects
- Logical expressions/ Control Flow
- String Processing
- Error handling
- SQL
Checklist
- An interface models the behaviors of an abstract type.
- Only declare types and methods in interfaces.
- Use classes as blueprints to create objects.
- Think interfaces first; abstract classes second.
- Use utility classes for basic functions.
- Inheritance models an is-a relationship.
- Avoid deep inheritance hierarchies.
- Design for inheritance or disallow it.
- Prefer composition to inheritance.
- Don’t mix stateful and stateless paradigms in the same class.
- Use global classes by default. Persönliches Exemplar für Abat IT3 Classes and Interfaces 94
- Class members visibility should be minimized.
- Consider making immutable attributes read-only.
- Only use read-only attributes in certain situations.
- Declare global classes constructors in the PUBLIC SECTION.
- Declare local classes constructors in the correct section.
- Use dependency injection to decouple from underlying resources.
- Consider static creation methods. Only use singleton when multiple instances don’t make sense.
- Prefer NEW to CREATE OBJECT.
- Aim for as few input parameters as possible.
- Split the behavior instead of adding optional parameters.
- Use a preferred parameter sparingly.
- Create separate methods instead of using Boolean parameters.
- Minimize the number of EXPORTING parameters.
- Prefer RETURNING to EXPORTING.
- Consider using the result as the name of RETURNING parameters.
- Use CHANGING parameters sparingly.
- Avoid using more than one kind of output parameter.
- Do not reassign IMPORTING parameters passed by value.
- Mind the semantics of EXPORTING parameters, prefer pass-by-value.
- Pass-by-value CHANGING parameters don’t make sense.
Working with variables
Constants
IF component->type = cl_component_type=>element. ASSIGN COMPONENT component->name OF structure STRUCTURE to FIELDSYMBOL(). ELSEIF component->type = cl_component_type=>structure. “Do something else. ENDIF.Constants grouping
INTERFACE if_message_constants PUBLIC.
CONSTANTS:
BEGIN OF severity,
warning TYPE msgty VALUE ‘W’.
error TYPE msgty VALUE ‘E’.
system TYPE msgty VALUE ‘S’.
END OF severity.
CONSTANTS:
BEGIN OF msgid_v1,
save_successfull TYPE msgno VALUE ‘001’.
save_failed TYPE msgno VALUE ‘006’.
END OF msgid_v1.
ENDINTERFACE.
String Building
DATA(message) = `Received an unexpected HTTP ` && status_code &&
` with message ` && text.
DATA(message) = |Received HTTP code { status_code } with message { text }|.
Boolean functions
boolc, boolx and xsdbool IF boolc( lv_val1 < lv_val2 ) = |X|. … ENDIF.XSDBOOL for Inline Decisions
DATA(has_entries) = XSDBOOL( messages IS NOT INITIAL ) is the same as
IF messages IS INITIAL.
has_entries = abap_false.
ELSE.
has_entries = abap_true.
ENDIF.
String Expression
s1 = 'ab' && 'cd'.
Substring
The return codes of the following function calls are: "CD", "CDEFGH", "EFGH", "AB", and "ABCD".- result = substring( val = 'ABCDEFGH' off = 2 len = 2 ).
- result = substring_from( val = 'ABCDEFGH' sub = 'CD' ).
- result = substring_after( val = 'ABCDEFGH' sub = 'CD' ).
- result = substring_before( val = 'ABCDEFGH' sub = 'CD' ).
- result = substring_to( val = 'ABCDEFGH' sub = 'CD' ).
Conditional operators
DATA(lv_status) = COND char5( WHEN sy-datum LT lv_begin THEN ‘EARLY’
WHEN sy-datum GT lv_end THEN ‘LATE’ ELSE ‘OK’ ).DATA(text) = NEW class( )->meth( SWITCH #( sy-langu WHEN ‘D’ THEN `DE`
WHEN ‘E’ THEN `EN`).
Value operator
Variables: VALUE dtype|#( )
Structures: VALUE dtype|#( comp1 = a1 comp2 = a2 … )
Tables: VALUE dtype|#( ( … ) ( … ) … ) …
Structures: VALUE dtype|#( comp1 = a1 comp2 = a2 … )
Tables: VALUE dtype|#( ( … ) ( … ) … ) …
Working with internal tables
Standard tables are typically used when individual entries can be accessed via their
index. The index of a table entry provides the fastest possible way to access an entry.
READ TABLE employees REFERENCE INTO employee INDEX 4
Another way to access the rows of a standard table would be by their primary keys.
READ TABLE employees REFERENCE INTO employee WITH TABLE KEY id = 'E101'.
If sorted access to standard tables is required, then entries can be sorted either in
ascending or descending order by one or more columns using a SORT statement.
READ TABLE employees REFERENCE INTO employee WITH KEY id = 'E101' BINARY SEARCH.
Sorted tables should be used for larger tables, whose entries always need to be sorted,
right from the time of its creation.
DATA employees TYPE SORTED TABLE OF employee WITH UNIQUE KEY id.
Hashed tables should be used only for large tables. The entries in these tables are filled
in a single step, never modified, and are often read using their key.
Hashed tables should be used only for large tables. The entries in these tables are filled
in a single step, never modified, and are often read using their key.
READ TABLE employees REFERENCE INTO employee WITH TABLE KEY id = 'E107'.
Therefore, the usage of DEFAULT KEY must be avoided. A better approach would be one of
the following options:
- Explicitly specify the components of the key, for example, with the following statement: DATA employees TYPE STANDARD TABLE OF employee WITH NON-UNIQUE KEY id cost_ center.
- Use EMPTY KEY if no key is required, for example, with the following statement: DATA employees TYPE STANDARD TABLE OF employee WITH EMPTY KEY.
Append or Insert
The APPEND statement appends one or more rows to an internal index table, which
inserts new rows at the end of the internal table, with respect to the primary table
index. You can use the APPEND statement in the following ways (does not work for Hashed )
- APPEND ROWS OF new_employees TO employees.
- APPEND new_employee TO employees.
The INSERT statement adds one or more rows to an internal table at any specified position
- INSERT new_employee INTO employees [INDEX 18].
- INSERT new_employee INTO employees [INDEX 18].
The insertion of new rows is executed successfully only after validating all existing
unique table keys, including the primary table key and multiple unique secondary
table keys, if present. The system handles any duplicate entries by setting system fields,
such as sy-subrc, or by raising appropriate exceptions.
Verifying the Existence of a Row
READ TABLE employees TRANSPORTING NO FIELDS WITH KEY first_name = 'Jason'.
IF sy-subrc = 0.
" Do Something
ENDIF.
LOOP AT employees REFERENCE INTO employee WHERE first_name = 'Jason'.
ENDLOOP.
IF sy-subrc = 0.
" Do Something
ENDIF.
To check the existence of a row by its one (and only one) key:
IF LINE_EXISTS( employees[ key = 'E101' ] ).
To check the existence of a row by any of its column names:
IF LINE_EXISTS( employees[ first_name = 'Jason' ] ).
Retrieving Table Contents
Reading All Rows Matching a Condition using LOOP AT and NESTED IF Iteratively
LOOP AT employees REFERENCE INTO employee.
IF employee->location = 'London'.
" Do Something
ENDIF.
ENDLOOP.
Reading a Single Row Matching a Condition Using READ TABLE
READ TABLE employees REFERENCE INTO employee WITH KEY location = 'London'.
IF sy-subrc = 0.
" Do something
ENDIF.
Reading All Rows Matching a Condition Using LOOP AT and Nested IF
LOOP AT employees REFERENCE INTO employee.
IF employee->location = 'Sydney'.
" Do Something
ENDIF.
ENDLOOP.
Reading All Rows Matching a Condition Using LOOP AT and WHERE
LOOP AT employees REFERENCE INTO employee WHERE location = 'Sydney'.
" Do Something
ENDLOOP.
Reading a Row and Then Reacting to the Exception If It Fails
TRY.
DATA(row) = employees[ id = 'E106' ].
CATCH cx_sy_itab_line_not_found.
RAISE EXCEPTION NEW my_data_not_found( ).
ENDTRY.
Block Processing of Table Rows and Single Row Operations
INSERT LINES OF old_employees FROM 10 TO 25 INTO employees.
Reading Multiple Consecutive Rows of a Table Using LOOP AT
LOOP AT employees ASSIGNING <employee> FROM 9 TO 16 WHERE manager = ‘Helen’.
<employee>-manager = ‘Michael’.
ENDLOOP.
DESCRIBE TABLE and Table Function LINES
DESCRIBE TABLE employees LINES no_of_employees.
IF no_of_employees = 0.
" Do Something
ENDIF.
IF LINES( employees ) = 0.
" Do Something
ENDIF.
Declaration
- In standard tables, only the addition NON-UNIQUE KEY can be specified. If uniqueness is not specified, this is added implicitly. The addition UNIQUE KEY cannot be specified.
- In sorted tables, one of the two additions UNIQUE KEY or NON-UNIQUE KEY must be specified.
- In hashed tables, the addition UNIQUE KEY must be specified.
Defines a sorted table with a primary key without an explicitly specified name.
DATA sbook_tab
TYPE SORTED TABLE
OF sbook
WITH UNIQUE KEY carrid connid fldate bookid.
DATA sbook_tab
TYPE SORTED TABLE
OF sbook
WITH UNIQUE KEY primary_key
COMPONENTS carrid connid fldate bookid.
Define a sorted table with a primary key, which an alias name is defined for.
DATA sbook_tab
TYPE SORTED TABLE
OF sbook
WITH UNIQUE KEY primary_key ALIAS full_table_key
COMPONENTS carrid connid fldate bookid.
Declares an internal hashed table. The row type corresponds to the structure of the database table SPFLI. Two key fields are defined for the primary table key. The other statements demonstrate how the table is filled with rows from database table SPFLI and how a row is read.
DATA: spfli_tab TYPE HASHED TABLE OF spfli
WITH UNIQUE KEY carrid connid,
spfli_wa LIKE LINE OF spfli_tab.
SELECT *
FROM spfli
WHERE carrid = 'LH'
INTO TABLE @spfli_tab.
spfli_wa = spfli_tab[ KEY primary_key carrid = 'LH' connid = '0400' ].
Ranges table is declared, filled, and evaluated in the WHERE condition of a SELECT statement.
DATA carrid_range TYPE RANGE OF spfli-carrid.
carrid_range = VALUE #(
( sign = 'I' option = 'BT' low = 'AA' high = 'LH') ).
SELECT *
FROM spfli
WHERE carrid IN @carrid_range
INTO TABLE @DATA(spfli_tab).
Looping Variables
DATA(messages_from_order_processing) = order_processor->get_messages( ).
LOOP AT messages_from_order_processing ASSIGNING FIELD-SYMBOL(<message_from_
order_processing>).
IF highest_severity > <message_from_order_processing>-severity.
highest_severity = <message_from_order_processing>-severity.
ENDIF.
ENDLOOP
DATA(messages_from_order_processing) = order_processor->get_messages( ).
LOOP AT messages_from_order_processing
REFERENCE INTO DATA(message_from_order_processing).
IF highest_severity > message_from_order_processing->severity.
highest_severity = message_from_order_processing->severity.
ENDIF.
ENDLOOP.
Field symbols and references have similar impacts on readability, but a field symbol
has advantages in terms of execution performance and therefore is recommended.
Mesh
Cогласно определению, меш — это специальная структура. Компоненты этой структуры называют узлами. Узлы являются либо структурированными внутренними таблицами, либо ссылочными переменными, которые указывают на структурированные внутренние таблицы.Meshes represent a new type
BEGIN OF t_manager,
manager_name TYPE char10,
salary TYPE int4,
END OF t_manager,
manager_name TYPE char10,
salary TYPE int4,
END OF t_manager,
tt_manager TYPE SORTED TABLE OF t_manager WITH UNIQUE KEY manager_name.
2) TYPES:
BEGIN OF t_developer,
name TYPE char10,
salary TYPE int4,
manager_name TYPE char10,
END OF t_developer,
name TYPE char10,
salary TYPE int4,
manager_name TYPE char10,
END OF t_developer,
tt_developer TYPE SORTED TABLE OF t_developer WITH UNIQUE KEY name.
3) TYPES:
BEGIN OF MESH tm_team, managers TYPE tt_manager
ASSOCIATION my_employee TO developers ON manager = name,
developers TYPE tt_developer
ASSOCIATION my_manager TO managers ON name = manager,
END OF MESH tm_team.
APPEND VALUE #( name = 'Tom' salary = 2000 manager_name = 'Thomas') TO lt_developer.
APPEND VALUE #( manager_name = 'Jason' salary = 3000 ) TO lt_manager.
APPEND VALUE #( manager_name = 'Thomas' salary = 3200 ) TO lt_manager.
ls_crm_team-developers = lt_developer.
ls_crm_team-managers = lt_manager.
DATA(line) = ls_crm_team-developers\my_manager[ ls_crm_team-developers[ name = 'Jerry' ] ].
Dynamic where condition in loop
LOOP AT itab ASSIGNING <fs> WHERE (cond) ENDLOOP
Get Index of a row using line_index
SYNTAX: <lv_index> = line_index( <ITAB> [ <COLUMN1> = <VALUE> <COLUMN2> = <VALUE> ... ] ).
Assign a component of selected table line to a field symbol
TRY.
assign component 4 of structure it_abc[ 2 ] to field-symbol(<fs_abc_comp>).
CATCH cx_sy_itab_line_not_found.
...
ENDTRY.
assign component 4 of structure it_abc[ 2 ] to field-symbol(<fs_abc_comp>).
CATCH cx_sy_itab_line_not_found.
...
ENDTRY.
Get the line from the internal table
DATA(ls_flight) = flight_tab[ KEY id COMPONENTS carrid = 'TA' connid = '0942' ]
-> Exception CX_SY_ITAB_LINE_NOT_FOUND
Assign a line from an internal table to a field symbol
TRY.assign it_abc[ 1 ]-it_xyz[ 1 ] to field-symbol(<fs_abc_line>).
CATCH cx_sy_itab_line_not_found.
...
ENDTRY.
Нужно обязательно проверить sy-subrc и IS ASSIGNED
*instead of making a LOOP inside LOOP between two tables
With REDUCE it is possible to do a mathematical operation grouping by the items of a certain table for example. An example:
Check if the line exists in an internal table
IF line_exists( it_abc[ key = 'X' ] ).
Count the number of lines in an internal table
- Without condition: ASSERT lines( itab ) = lines.
- With condition: DATA(lv_count) = REDUCE i( INIT i = 0 FOR wa IN lt_mara WHERE ( aenam EQ 'MARSLAN' ) NEXT i = i + 1 ).
Internal Table Queries with REDUCE
The purpose of REDUCE is to reduce an internal table into a single variable (e.g., a sum
or a count), which already demonstrates that REDUCE is not a trivial operator.
With REDUCE it is possible to do a mathematical operation grouping by the items of a certain table for example. An example:
LOOP AT it_ekpo
ASSIGNING FIELD-SYMBOL(<fs_ekpo>).
<fs_ekpo>-netwr = REDUCE netwr( INIT val TYPE netwr
FOR wa IN
FILTER #( it_komv
USING KEY key_kposn
WHERE kposn EQ CONV #( <fs_ekpo>-ebelp ) )
NEXT val = val + wa-kwert ).
ENDLOOP.
ASSIGNING FIELD-SYMBOL(<fs_ekpo>).
<fs_ekpo>-netwr = REDUCE netwr( INIT val TYPE netwr
FOR wa IN
FILTER #( it_komv
USING KEY key_kposn
WHERE kposn EQ CONV #( <fs_ekpo>-ebelp ) )
NEXT val = val + wa-kwert ).
ENDLOOP.
Iteration Using REDUCE with Short Names
DATA(sum) = REDUCE #(
SUM = 0
FOR i = 1 UNTIL i > lines( items )
NEXT sum = sum + items[ i ]-amount ).
Reduce for Inline Calculation
DATA(net_amount) = REDUCE #( INIT amount TYPE netwr
FOR item IN sales_order-items
NEXT amount = amount + item-net_amount ).
Grouping Internal Tables
LOOP AT lt_open_items INTO DATA( ls_open_items) GROUP BY ( kunnr = ls_open_items-kunnr ) ASCENDING ASSIGNING FIELD-SYMBOL( <customer_items> ).
CLEAR lt_open_items_for_customer.
LOOP AT GROUP <customer_items> ASSIGNING FIELD-SYMBOL( <ls_items>.
APPEND <ls_items> TO lt_open_items_for_customer.
ENDLOOP. "Items for one customer
process_open_items->( EXPORTING it_items = lt_open_items_for_customer CHANGING ct_proposals = ct_proposals ).
process_open_items->( EXPORTING it_items = lt_open_items_for_customer CHANGING ct_proposals = ct_proposals ).
ENDLOOP. "Grouped Open Items
Stories...
Change Purchase Orders
To update a list of purchase order items via BAPI_PO_CHANGE each BAPI call is done with the collected list of items belonging to a single purchase order. I used to do it like this
METHOD update.
DATA ls_item LIKE LINE OF it_item.
DATA lt_item TYPE tt_item.
LOOP AT it_item INTO ls_item.
AT NEW ebeln.
CLEAR lt_item.
ENDAT.
APPEND ls_item TO lt_item.
AT END OF ebeln.
update_po( lt_item ).
ENDAT.
ENDLOOP.
ENDMETHOD. "update
I can now create a GROUP at Purchase Order level:
METHOD update.
LOOP AT it_item INTO DATA(ls_item) GROUP BY ls_item-ebeln INTO DATA(lv_ebeln).
update_po( VALUE #( FOR g IN GROUP lv_ebeln ( g ) ) ).
ENDLOOP.
ENDMETHOD.
Priority Queue for Messages
The BAPI call returns messages of type A, E, X, I, W, S. I have pick one to be displayed in an ALV output, I do not care which info/warning/status message is selected, but if error messages (A, E, X) should be preferred.
The design is to order the return messages according to type and pick up the message with the highest priority for display. In a first approach, I assume type X does not occur so lexical ordering AEISWX is appropriate:
METHOD get_current_message.
DATA lt_return LIKE it_return.
DATA ls_return TYPE bapiret2.
* Priority
SORT lt_return BY type.
LOOP AT lt_return INTO ls_return.
EXIT.
ENDLOOP.
rv_message = ls_return-message.
ENDMETHOD. "get_current_message
With GROUP BY it would be:
METHOD get_current_message.
* Priority
LOOP AT it_return INTO DATA(ls_return) GROUP BY ls_return-type.
rv_message = ls_return-message.
RETURN.
ENDLOOP.
ENDMETHOD.
And it is now easy to roll out my custom ordering logic
METHOD priority.
rv_prio = SWITCH #( iv_type WHEN 'A' THEN 0
WHEN 'X' THEN 1
WHEN 'E' THEN 2
WHEN 'W' THEN 3
WHEN 'S' THEN 4
ELSE 5 ).
ENDMETHOD.
METHOD get_current_message.
LOOP AT it_return INTO DATA(ls_return) GROUP BY priority( ls_return-type ).
rv_message = ls_return-message.
RETURN.
ENDLOOP.
ENDMETHOD.
Wait, I could use the standard translate( ) function here
METHOD get_current_message.
LOOP AT it_return INTO DATA(ls_return)
GROUP BY translate( val = ls_return-type from = `AXEWS ` to = `012345` ).
rv_message = ls_return-message.
RETURN.
ENDLOOP.
ENDMETHOD.
Filter IDocs by Segment
To filter out entries containing a given segment from a list of IDocs, I could do
* we check witch idoc message types contains the required segment
lt_edidc[] = ct_edidc[].
SORT lt_edidc BY idoctp cimtyp.
DELETE ADJACENT DUPLICATES FROM lt_edidc COMPARING idoctp cimtyp.
LOOP AT lt_edidc ASSIGNING <ls_edidc>.
REFRESH lt_syntax.
CALL FUNCTION 'EDI_IDOC_SYNTAX_GET'
EXPORTING
pi_idoctyp = <ls_edidc>-idoctp
pi_cimtyp = <ls_edidc>-cimtyp
TABLES
pt_syntax_table = lt_syntax
EXCEPTIONS
OTHERS = 0.
READ TABLE lt_syntax TRANSPORTING NO FIELDS WITH KEY segtyp = iv_segnam.
IF sy-subrc NE 0.
DELETE ct_edidc WHERE idoctp = <ls_edidc>-idoctp
AND cimtyp = <ls_edidc>-cimtyp.
ENDIF.
ENDLOOP.
But now that I think in groups, I will GROUP BY:
METHOD filter_by_segment.
DATA lt_syntax TYPE STANDARD TABLE OF edi_iapi06.
LOOP AT ct_edidc INTO DATA(ls_edidc)
GROUP BY ( idoctp = ls_edidc-idoctp
cimtyp = ls_edidc-cimtyp )
WITHOUT MEMBERS ASSIGNING FIELD-SYMBOL(<ls_group>).
CLEAR lt_syntax[].
CALL FUNCTION 'EDI_IDOC_SYNTAX_GET'
EXPORTING
pi_idoctyp = <ls_group>-idoctp
pi_cimtyp = <ls_group>-cimtyp
TABLES
pt_syntax_table = lt_syntax
EXCEPTIONS
OTHERS = 0.
CHECK NOT line_exists( lt_syntax[ segtyp = iv_segnam ] ).
DELETE ct_edidc WHERE idoctp = <ls_group>-idoctp
AND cimtyp = <ls_group>-cimtyp.
ENDLOOP.
ENDMETHOD.
The WITHOUT MEMBERS addition is needed because entries in the GROUP are changed.
Report Contract Release Documentation
From a list of purchasing contract items I compare the release history with the sum of invoices. A s those are value contracts an aggregation of the items is made to compare at contract header level. Without GROUP BY, LOOP AT NEW helps:
METHOD add_data.
DATA lv_logsy TYPE logsys.
DATA ls_totals TYPE lcl_contract=>ts_totals.
DATA lv_error TYPE flag.
DATA lo_contract TYPE REF TO lcl_contract.
FIELD-SYMBOLS <ls_ekab> TYPE ts_ekab.
CHECK mt_ekab IS NOT INITIAL.
lv_logsy = get_logsys( ).
CREATE OBJECT lo_contract
EXPORTING
io_ekab = me
io_rfc = io_rfc.
* Value Contracts
LOOP AT mt_ekab ASSIGNING <ls_ekab>.
AT NEW konnr.
lo_contract->consumption( EXPORTING iv_konnr = <ls_ekab>-konnr
IMPORTING es_totals = ls_totals
ev_error = lv_error ).
ENDAT.
<ls_ekab>-logsy = lv_logsy.
<ls_ekab>-wrbtr = read_history( <ls_ekab> ).
IF <ls_ekab>-erekz EQ abap_true.
<ls_ekab>-total = <ls_ekab>-wrbtr.
ELSE.
<ls_ekab>-total = <ls_ekab>-netwr.
ENDIF.
IF lv_error EQ abap_false.
<ls_ekab>-rewwb = ls_totals-value.
ENDIF.
ENDLOOP.
ENDMETHOD.
But grouping with GROUP BY is simpler to grasp
METHOD add_data.
CHECK mt_ekab IS NOT INITIAL.
DATA(lv_logsy) = get_logsys( ).
DATA(lo_contract) = NEW lcl_contract( io_ekab = me
io_rfc = io_rfc ).
LOOP AT mt_ekab INTO DATA(ls_ekab) GROUP BY ls_ekab-konnr INTO DATA(lv_konnr).
lo_contract->consumption( EXPORTING iv_konnr = lv_konnr
IMPORTING es_totals = DATA(ls_totals)
ev_error = DATA(lv_error) ).
LOOP AT GROUP lv_konnr ASSIGNING FIELD-SYMBOL(<ls_ekab>).
<ls_ekab>-logsy = lv_logsy.
<ls_ekab>-wrbtr = read_history( <ls_ekab> ).
<ls_ekab>-total = SWITCH #( <ls_ekab>-erekz WHEN 'X' THEN <ls_ekab>-wrbtr
ELSE ls_ekab>-netwr ).
IF lv_error EQ abap_false.
<ls_ekab>-rewwb = ls_totals-value.
ENDIF.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
Aggregate Function SUM like SQL’s GROUP BY
Next – building a subtotal like the aggregate GROUP BY for database tables
METHOD convert.
CLEAR et_comm.
LOOP AT it_ekab ASSIGNING FIELD-SYMBOL(<ls_ekab>)
GROUP BY ( ekgrp2 = <ls_ekab>-ekgrp2
konnr = <ls_ekab>-konnr
logsy = <ls_ekab>-logsy ) INTO DATA(ls_group).
DATA(ls_comm) = VALUE ts_comm( ekgrp2 = ls_group-ekgrp2
konnr = ls_group-konnr
logsy = ls_group-logsy ).
LOOP AT GROUP ls_group INTO DATA(ls_ekab).
ls_comm-ktwrt = ls_ekab-ktwrt. " Contract target value
ls_comm-total = ls_ekab-rewwb. " Contract sum of releases
ls_comm-kwaers = ls_ekab-kwaers. " Contract currency
ADD ls_ekab-total TO ls_comm-wrbtr. " Sum invoices or PO value if no EREKZ
ENDLOOP.
ls_comm-exceeded = xsdbool( ls_comm-wrbtr + ls_ekab-rewwb > ls_comm-ktwrt ).
APPEND ls_comm TO et_comm.
ENDLOOP.
ENDMETHOD.
Now, downgrading is pain,
METHOD convert.
DATA ls_comm TYPE ts_comm.
FIELD-SYMBOLS <ls_ekab> TYPE ts_ekab.
CLEAR et_comm.
TYPES: BEGIN OF ts_key,
ekgrp2 TYPE ekgrp,
konnr TYPE konnr,
logsy TYPE logsys,
END OF ts_key.
DATA lt_key TYPE SORTED TABLE OF ts_key WITH UNIQUE KEY ekgrp2 konnr logsy.
DATA ls_key LIKE LINE OF lt_key.
LOOP AT mt_ekab ASSIGNING <ls_ekab>.
MOVE-CORRESPONDING <ls_ekab> TO ls_key.
INSERT ls_key INTO TABLE lt_key.
ENDLOOP.
LOOP AT lt_key INTO ls_key.
LOOP AT it_ekab ASSIGNING <ls_ekab> WHERE ekgrp2 = ls_key-ekgrp2
AND konnr = ls_key-konnr
AND logsy = ls_key-logsy.
AT FIRST.
MOVE-CORRESPONDING <ls_ekab> TO ls_comm.
ENDAT.
ls_comm-ktwrt = <ls_ekab>-ktwrt. " Contract target value
ls_comm-total = <ls_ekab>-rewwb. " Contract sum of releases
ls_comm-kwaers = <ls_ekab>-kwaers. " Contract currency
ADD <ls_ekab>-total TO ls_comm-wrbtr. " Sum invoices or PO value if no EREKZ
AT LAST.
DATA lv_sum LIKE ls_comm-wrbtr.
lv_sum = ls_comm-wrbtr + <ls_ekab>-rewwb.
IF lv_sum > ls_comm-ktwrt.
ls_comm-to_be_checked = abap_true.
ELSE.
ls_comm-to_be_checked = abap_false.
ENDIF.
APPEND ls_comm TO et_comm.
ENDAT.
ENDLOOP.
ENDLOOP.
ENDMETHOD. "convert
Expressions
Modern ABAP introduced expressions, creating a conflict almost by design as new features were hitherto added to ABAP as new statements.
◉ A statement does something: statements work on data; they are imperative and applied in sequence.
◉ An expression returns a value: pure functions (without side effect) are composable. But note ABAP does not try to be a functional language, the type system does not support complex/algebraic data types
So GROUP BY also has an expression oriented form.
METHOD convert.
et_comm = VALUE #( FOR GROUPS ls_group OF <ls_ekab> IN it_ekab
GROUP BY ( ekgrp2 = <ls_ekab>-ekgrp2
konnr = <ls_ekab>-konnr
logsy = <ls_ekab>-logsy
ktwrt = <ls_ekab>-ktwrt " target value
total = <ls_ekab>-rewwb " sum of releases
kwaers = <ls_ekab>-kwaers " Contract currency
exceeded = <ls_ekab>-exceeded
rewwb = <ls_ekab>-rewwb
count = GROUP SIZE )
( VALUE #( LET lv_sum = REDUCE wrbtr( INIT lv_val TYPE wrbtr
FOR ls_ekab IN GROUP ls_group
NEXT lv_val = lv_val + ls_ekab-total )
IN BASE CORRESPONDING ts_comm( ls_group )
wrbtr = lv_sum " Sum invoices or PO value if no EREKZ
to_be_checked = xsdbool( lv_sum + ls_group-rewwb > ls_group-ktwrt ) ) ) ).
ENDMETHOD.
Extracting One Table from Another (HASHED or SORTED table)
DATA( lt_average ) = FILTER #( lt_all USING KEY field_key WHERE col_1 > 25 AND col2 < 75 ).
DATA(extract) = FILTER #( spfli_tab IN filter_tab WHERE cityfrom = cityfrom AND cityto = cityto ).DATA: lt_flights_all TYPE STANDARD TABLE OF spfli
WITH NON-UNIQUE SORTED KEY carrid
COMPONENTS carrid,
lt_flight_lh TYPE STANDARD TABLE OF spfli.
SELECT * FROM spfli INTO TABLE @lt_flights_all.
lt_flight_lh = FILTER #( lt_flights_all USING KEY carrid WHERE carrid = 'LH ' ).
По нескольким полям
DATA: lt_splfi_key TYPE SORTED TABLE OF spfli WITH NON-UNIQUE KEY carrid connid.
DATA: lt_filter_tab_new TYPE SORTED TABLE OF spfli WITH NON-UNIQUE KEY carrid connid.
SELECT * FROM spfli INTO TABLE lt_splfi_key.
lt_filter_tab_new = VALUE spfli_tab( carrid = 'LH ' ( connid = '0401' ) ( connid = '0402' ) ).
DATA(lt_splfi_result_new) = FILTER #( lt_splfi_key IN lt_filter_tab_new
WHERE carrid = carrid AND connid = connid ).
For each
DATA messages TYPE SORTED TABLE OF t100 WITH NON-UNIQUE KEY sprsl msgnr.SELECT * FROM t100 WHERE arbgb = 'SABAPDEMOS' INTO TABLE @messages.
value_tab = VALUE #( FOR wa IN messages WHERE ( sprsl = 'E' ) ( wa ) ).
ASSERT value_tab = FILTER #( messages WHERE sprsl = 'E' ).
Working with references and field-symbols
Example §1
DATA(lr_ref) = REF #( lt_itab[ bukrs = ’1234’ belnr = ‘1234567890’ gjahr = ‘2018’ ] ).Example §2
DATA: lz_counter TYPE REF TO i.
DATA: lt_sa0_key LIKE gt_sa0_key.
lt_sa0_key = CORRESPONDING #( it_sa0_key DISCARDING DUPLICATES ).
lz_counter = REF #( lt_sa0_key[ 1 ]-counter ).
lz_counter->* = lines( it_sa0_key ).
DATA: lt_sa0_key LIKE gt_sa0_key.
lt_sa0_key = CORRESPONDING #( it_sa0_key DISCARDING DUPLICATES ).
lz_counter = REF #( lt_sa0_key[ 1 ]-counter ).
lz_counter->* = lines( it_sa0_key ).
Add a line into an internal table
INSERT VALUE #( ... ) INTO TABLE itab.
Comment: INSERT INTO TABLE works with all tables and key types, thus making it easier for you to refactor the table's type and key definitions if your performance requirements change.
Add one more line in a loop with the base operator
lt_items = VALUE #( BASE lt_items ( itm_number = <ls_pos>-itm_number
material = <ls_pos>-materialnet_price = <ls_pos>-net_pricenet_value = <ls_pos>-net_valuereq_qty = <ls_pos>-req_qtys_proc_ind = <ls_pos>-s_proc_indsales_unit = <ls_pos>-sales_unitshort_text = <ls_pos>-short_text ) ).
Filling Internal Tables from Other Tables Using FOR
Creating Short-Lived Variables Using LET
DATA( ld_line ) = VALUE string(
LET
temp_var1 = lo_iterator->get_next( )
temp_var2 = lt_table[ sy-index ]-key_field
date_string = |{ temp_var1 } { temp_var2} |.
WRITE:/ ld_line.
ENDDO.
*These variables (temp_var1, temp_var2) only exist while creating data using constructor expressions.
Virtual sort
data gt_fio type table of gs_fio.
append value #( pernr = '90000001' fio = 'Ivanov I.I.' bukrs = '0001' ) to gt_fio.
append value #( pernr = '00000002' fio = 'Petrov P.P.' bukrs = '0002' ) to gt_fio.
append value #( pernr = '90000999' fio = 'Sidorov S.S.' bukrs = '0000' ) to gt_fio.
append value #( pernr = '00000001' fio = 'Ivanov A.A.' bukrs = '0001' ) to gt_fio.
append value #( pernr = '99999999' fio = 'Ivanov B.B.' bukrs = '9999' ) to gt_fio.
data(lt_index_sort) = cl_abap_itab_utilities=>virtual_sort(
im_virtual_source =
value #(
( source = ref #( gt_fio )
components =
value #( ( name = 'bukrs' descending = abap_true )
( name = 'fio' ) ) )
)
).
cl_demo_output=>display( lt_index_sort ).
Logical expressions
Control flow
COND Replacing IF Block
DATA(is_authorized) = COND #(
WHEN authroized_document_type = abap_true AND
authroized_organization = abap_true
THEN authorized
ELSE unauthorized ).
COND with Inline Field Symbol Assignment
DATA(businesspartner) = COND #(
WHEN type = employee
THEN
LET <id> = employees[ employee_id = employee_id ]-businesspartner
IN <id>
WHEN type = contractor
THEN
LET <id> = contractors[ contractor_id = contractor_id ]-businesspartner
IN <id> ).
Case Considering the Single-Responsibility Principle
CASE vehicle_type.
WHEN 'TRAIN'.
duration = calculate_train_duration( ).
WHEN 'PLANE'.
duration = calculate_plane_duration( ).
WHEN 'CAR'.
duration = calculate_car_duration( ).
ENDCASE.
SWITCH for Inline Assignment
DATA(id_to_display) = SWITCH string( id_type
WHEN 'EMPLOYEE' THEN partner-employee_id
WHEN 'EXT_WORK' THEN partner-assignment_id
WHEN 'CUSTOMER' THEN partner-customer_id
ELSE THROW unknown_type( ).
DO 1 TIMES to Avoid Multiple IFs
DO 1 TIMES.
DATA(messages) = precheck_input( input ).
CHECK NOT line_exists( messages[ type = error ] ).
prepare_processing(
EXPORTING input = input
IMPORTING prepared_input = data(prepared_input)
messages = messages ).
CHECK NOT line_exists( messages[ type = error ] ).
messages = process( prepared_input ).
CHECK NOT line_exists( messages[ type = error ] ).
ENDO.
result = COND #( WHEN line_exists( messages[ type = error ] )
THEN failed
ELSE success ).
Cond
DATA(messages) = input_complete_processing( input ).
result = COND #( WHEN line_exists( messages[ type = error ] )
THEN failed
ELSE success ).
Calculation in logical expressions
CHECK lcl=>mi( 1 ) + abs( -2 ) >= 3.CHECK lcl=>mx( x1 ) BIT-OR x2 <> BIT-SET( 4 ).
CHECK `ab` && 'cd' CN 'xyz'.
Corresponding
(0) With a structure: struct2 = CORRESPONDING #( struct1 MAPPING b1 = a1 ).(1) DATA(lt_malzeme) = CORRESPONDING tt_malz( lt_mara ).
(2) DATA(lt_malzeme) = CORRESPONDING tt_malz( lt_mara MAPPING material = matnr EXCEPT meins ).
(3) DATA(itab2) = VALUE t_itab2
( FOR wa IN itab1 WHERE ( col1 < 10 ) ( col1 = wa-col2 col2 = wa-col3 ) ).
( FOR wa IN itab1 WHERE ( col1 < 10 ) ( col1 = wa-col2 col2 = wa-col3 ) ).
(4.1) With the BASE (structure)
struct2 = CORRESPONDING #( BASE ( struct2 ) struct1 ). (4.1) With the BASE (table)
itab = VALUE #( ( 1 ) ( 2 ) ( 3 ) ).
itab = VALUE #( BASE itab ( 4 ) ( 5 ) ( 6 ) ).
The result of the second expression is initialized with itab and then the other lines are added.
After the second assignment, itab contains 1, 2, 3, 4, 5, 6.
String Processing
Removing Leading Zeroes via a Formatting Option
ld_message = |{ ld_delivery ALPHA = OUT }|.Building Up a String Using Pipes
LD_RESULT = |Local Object { ld_number } / { ld_status }|.String Templates
lv_string = | Hello my Name is { SY-UNAME } and today is { SY-DATUM date = user } |.Working with the references
DATA(gz_matnr) = REF #( gv_matnr )
DATA(slfight_ref) = REF #( sflight_tab[ KEY primary_key COMPONENTS carrid = p_carrid connid = p_connid fldate = p_fldate ] )
Grouping with LOOP
LOOP AT GROUP BY
Grouping by one columnLOOP AT spfli_tab INTO wa
GROUP BY wa-carrid.
out->write( wa-carrid ).
ENDLOOP.
Members of one column groups
LOOP AT spfli_tab INTO wa
GROUP BY wa-carrid.
CLEAR members.
LOOP AT GROUP wa INTO member.
members = VALUE #( BASE members ( member ) ).
ENDLOOP.
out->write( members ).
ENDLOOP.
Grouping by one column
LOOP AT spfli_tab INTO wa
GROUP BY wa-carrid
INTO DATA(key).
out->write( key ).
ENDLOOP.
LOOP AT spfli_tab INTO wa
GROUP BY wa-carrid
INTO key.
CLEAR members.
LOOP AT GROUP key INTO member.
members = VALUE #( BASE members ( member ) ).
ENDLOOP.
out->write( members ).
ENDLOOP.
GROUP BY wa-carrid
INTO DATA(key).
out->write( key ).
ENDLOOP.
Members of one column groups
GROUP BY wa-carrid
INTO key.
CLEAR members.
LOOP AT GROUP key INTO member.
members = VALUE #( BASE members ( member ) ).
ENDLOOP.
out->write( members ).
ENDLOOP.
Two column groups without members
LOOP AT spfli_tab INTO wa GROUP BY ( key1 = wa-carrid key2 = wa-airpfrom
index = GROUP INDEX
index = GROUP INDEX
size = GROUP SIZE ) WITHOUT MEMBERS
INTO DATA(keysplus).
out->write( keysplus ).
ENDLOOP.
INTO DATA(keysplus).
out->write( keysplus ).
ENDLOOP.
By a package
DATA(n) = 10.
DATA group LIKE itab.
LOOP AT itab INTO DATA(wa)
GROUP BY ( sy-tabix - 1 ) DIV n + 1.
CLEAR group.
LOOP AT GROUP wa ASSIGNING FIELD-SYMBOL(<wa>).
group = VALUE #( BASE group ( <wa> ) ).
ENDLOOP.
cl_demo_output=>write( group ).
ENDLOOP.
DATA group LIKE itab.
LOOP AT itab INTO DATA(wa)
GROUP BY ( sy-tabix - 1 ) DIV n + 1.
CLEAR group.
LOOP AT GROUP wa ASSIGNING FIELD-SYMBOL(<wa>).
group = VALUE #( BASE group ( <wa> ) ).
ENDLOOP.
cl_demo_output=>write( group ).
ENDLOOP.
Grouping with LOOP Using Column Values
LOOP AT flights INTO DATA(flight)GROUP BY ( carrier = flight-carrid cityfr = flight-cityfrom
size = GROUP SIZE index = GROUP INDEX )
ASCENDING REFERENCE INTO DATA(group_ref).
LOOP AT GROUP group_ref ASSIGNING FIELD-SYMBOL(<flight>).
members = VALUE #( BASE members ( <flight> ) ).
ENDLOOP.
ENDLOOP.
Grouping with LOOP Using a Comparison
DATA members LIKE numbers.
LOOP AT numbers INTO DATA(number)
GROUP BY COND string(
WHEN number <= threshold THEN |LE { threshold }|
ELSE |GT { threshold }| )
ASSIGNING FIELD-SYMBOL(<group>).
out->begin_section( <group> ).
CLEAR members.
LOOP AT GROUP <group> REFERENCE INTO DATA(member_ref).
members = VALUE #( BASE members ( member_ref->* ) ).
ENDLOOP.
Working with objects
Если исходящие параметры метода в Inline, то нужно обязательно проверить, чтобы они были не в LOOP. А еще лучше задать локальные пременные.
Enumeration
CLASS shirt DEFINITION. PUBLIC SECTION. TYPES:
BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl,
END OF ENUM tsize.
METHODS constructor IMPORTING size TYPE tsize. ... PRIVATE SECTION. DATA size TYPE tsize. ENDCLASS.
CLASS shirt IMPLEMENTATION.
METHOD constructor. me->size = size.
ENDMETHOD. ENDCLASS.
Enumeration types belong in classes like constants to the static attributes
TYPES BEGIN OF ENUM enum_type [STRUCTURE struc] [BASE TYPE dtype]. TYPES val1 [VALUE IS INITIAL], TYPES val2 [VALUE val], TYPES val3 [VALUE val], ... TYPES END OF ENUM enum_type [STRUCTURE struc].
CL_ABAP_ENUMDESCR
Static vs Private attribute
Instance attributes are created at a new memory location every single time. However, only one memory allocation is stored for a static attribute. A lifetime of a static attribute depends on the internal session of the program, not on a single user session or ABAP memory.If the dynamic type for CREATE OBJECT obj TYPE (type) does not match the static type, the exception CX_SY_CREATE_OBJECT_ERROR is thrown
DATA lo_obj TYPE REF TO zcl_class. lo_obj = NEW #( ).
WHEN TYPE lcl_class.
ENDCASE.
IF value IS SUPPLIED.
lo_obj->log_value( REF#( value )).
ENDIF.
Create an object of a class
DATA(lo_obj) = NEW zcl_class( ).DATA lo_obj TYPE REF TO zcl_class. lo_obj = NEW #( ).
Do casting to another object
DATA(lo_obj2) = CAST zcl_002( lo_obj1 ).Is Instance Of
IF lo_obj2 IS INSTANCE OF lo_obj1. " ... ENDIF.Case type of
CASE TYPE OF oref.WHEN TYPE lcl_class.
ENDCASE.
Default Ignore
INTERFACE zif_demo PUBLIC. METHODS: method1 DEFAULT IGNORE, method2.Using Constructor Operator REF to Fill a TYPE REF TO DATA Parameter
"VALUE is an IMPORTING parameter TYPE ANYIF value IS SUPPLIED.
lo_obj->log_value( REF#( value )).
ENDIF.
Local classes
- Definition part
******************************************
CLASS lcl_airplane DEFINITION.
*------------------------------ - Public section
*------------------------------
PUBLIC SECTION.
TYPES: t_name(25) TYPE c.
METHODS:
constructor,
set_attributes IMPORTING p_name TYPE t_name
p_planetype TYPE saplane-planetype,
display_attributes,
display_n_o_airplanes.
*------------------------------ - Private section
*------------------------------
PRIVATE SECTION.
* Private attributes
DATA: name(25) TYPE c,
planetype TYPE saplane-planetype.
* Private static attribute
CLASS-DATA n_o_airplanes TYPE i.
ENDCLASS.
****************************************** - Implementation part
******************************************
CLASS lcl_airplane IMPLEMENTATION.
METHOD constructor.
* Counts number of instances
n_o_airplanes = n_o_airplanes + 1.
ENDMETHOD.
METHOD set_attributes.
name = p_name.
planetype = p_planetype.
ENDMETHOD.
METHOD display_attributes.
Write:/ 'name ', name , 'plane type ', planetype.
ENDMETHOD.
METHOD display_n_o_airplanes.
WRITE: / 'No. planes:', n_o_airplanes.
ENDMETHOD.
ENDCLASS.
******************************************
*& Start of Selection
******************************************
START-OF-SELECTION. - Create reference to class lcl_airplane
DATA: airplane1 TYPE REF TO lcl_airplane,
airplane2 TYPE REF TO lcl_airplane.
CREATE OBJECT airplane1.
CALL METHOD: airplane1->display_n_o_airplanes.
CREATE OBJECT airplane2.
CALL METHOD: airplane1->set_attributes
EXPORTING p_name = 'Chidanand'
p_planetype = 'A380'.
******************************************
*& End of selection
******************************************
END-OF-SELECTION. - Using methods
CALL METHOD: airplane1->display_n_o_airplanes,
airplane1->display_attributes.
Error handling
MESSAGE addition to RAISE EXCEPTION RAISE EXCEPTION TYPE cx_dyn_t100 MESSAGE e888(sabapdemos) WITH 'I' 'am' 'an' 'Exception!Exceptions in conditional expressions with THROW DATA(iflag) = COND i( WHEN cflag = abap_true THEN 1 WHEN cflag = abap_false THEN 0 ELSE THROW cx_demo_dyn_t100( MESSAGE e888(sabapdemos) WITH 'Illegal value!') ) ).
Exception Class with textid
CLASS cx_order DEFINITION PUBLIC
INHERITING FROM cx_static_check FINAL CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_t100_dyn_msg .
INTERFACES if_t100_message .
CONSTANTS:
BEGIN OF cx_example,
msgid TYPE symsgid VALUE 'ORDER',
msgno TYPE symsgno VALUE '003',
attr1 TYPE scx_attrname VALUE 'order_id',
attr2 TYPE scx_attrname VALUE '',
attr3 TYPE scx_attrname VALUE '',
attr4 TYPE scx_attrname VALUE '',
END OF cx_example.
DATA order_id TYPE vbeln READ-ONLY.
METHODS constructor
IMPORTING
textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
order_id TYPE vbeln OPTIONAL.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS cx_example IMPLEMENTATION.
METHOD constructor ##ADT_SUPPRESS_GENERATION.
super->constructor( previous = previous ).
me->order_id = order_id.
CLEAR me->textid.
IF textid is initial.
if_t100_message~t100key = cx_example.
ELSE.
if_t100_message~t100key = textid.
ENDIF.
ENDMETHOD.
ENDCLASS.
Convert Function Module Exception to T100 Dynamic Exception
METHOD read_address.
CALL FUNCTION 'ADDRESS_GET'
EXPORTING
address_id = address_id
IMPORTING
complete_address = complete_address
EXCEPTIONS
address_invalid = 1
error_message = 2.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_function_call
MESSAGE ID sy-msgid
TYPE sy-msgty
NUMBER sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
ENDMETHOD.
Method Signature Raising Static Exception
CLASS cx_invalid_user_name DEFINITION
INHERITING FROM cx_static_check.
METHODS read_by_user name
IMPORTING
user_name TYPE string
RAISING
cx_invalid_username.
Raise Exception New
METHOD save.
IF status = inconsistent.
RAISE EXCEPTION NEW cx_business_error(
reason = co_inconsistent ).
ENDIF.
ENDMETHOD.
Raise Exception Type with Message
METHOD save.
IF status = inconsistent.
RAISE EXCEPTION TYPE cx_business_error
MESSAGE ID 'MSGID'
NUMBER 123
TYPE 'E'
WITH co_inconsistent.
ENDIF.
ENDMETHOD.
Catch Single Exception
TRY.
calculate_income( order ).
CATCH cx_currency_conversion INTO DATA(currency_exception).
send_exception_to_ui( currency_exception ).
ENDTRY.
Usage of IS INSTANCE OF in CATCH Block
TRY.
method_call( ).
CATCH cx_root INTO data(exception).
IF cx_root IS INSTANCE OF cx_specific_exception.
handle_specific_exceptions( exception ).
ELSE.
RAISE EXCEPTION exception.
ENDTRY.
Wrapping foreign_exception
METHOD calculate_tax.
DATA(calculation_enginge) = tax_factory=>get_enginge( ).
TRY.
tax = calculation_enginge->calculate(
amount = amount
country = country ).
CATCH cx_tax_ennginge_exception INTO DATA(foreign_exception)
RAISE EXCEPTION NEW own_exception_type(
previous = foreign_exception ).
ENDTRY.
ENDMETHOD.
CLEANUP for Exceptions
TRY.
TRY.
do_something( ).
CATCH exception_a.
do_something_else( ).
CLEANUP INTO DATA(exception).
first_do_this( exception ).
ENDTRY.
CATCH exception_b INTO DATA(exception_b).
error_handling( exception_b ).
ENDTRY.
Exceptions are for errors
Anti-Pattern: Exceptions Used in a Normal Flow
TRY.
read_order_from_db( order ).
CATCH INTO DATA(not_found_exception).
persist_new_order( order ).
ENDTRY.
Pattern: Check Instead of Forcing Errors
IF order_not_persisted( order ).
persist_new_order( order ).
ENDIF.
Converting Messages into Exceptions
RAISE EXCEPTION TYPE cx_demo_dyn_t100
MESSAGE ID sy-msgid
TYPE sy-msgty
NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
Passing the message to another Exception
...catch ycx_lo_batch_master into data(lx_exception).raise exception type ycx_mbgmcr_handler
message id lx_exception->if_t100_message~t100key-msgid
type 'E'
number lx_exception->if_t100_message~t100key-msgno
with lx_exception->if_t100_dyn_msg~msgv1
lx_exception->if_t100_dyn_msg~msgv2
lx_exception->if_t100_dyn_msg~msgv3
lx_exception->if_t100_dyn_msg~msgv4.
SQL
Virtual columns
select from SFLIGHT join SCARR on SFLIGHT~CARRID = SCARR~CARRID fields SFLIGHT~CARRID, SFLIGHT~CONNID, SFLIGHT~FLDATE, SCARR~CARRNAME, case when SFLIGHT~FLDATE < @SY-DATUM then @ABAP_TRUE end as OUTDATED into table @data(LT_SFLIGHT).
Join: select fields
select from SPFLIinner join SCARR on SPFLI~CARRID = SCARR~CARRID
fields SPFLI~*,
SCARR~CARRNAME
into table @data(LT_FLIGHTS_COMPLETE).select from SPFLI
Join: with Where parameter
left outer join SCARR on SPFLI~CARRID = SCARR~CARRID and
SPFLI~COUNTRYFR = @LK_COUNTRY_BY and
SPFLI~COUNTRYTO = @LK_COUNTRY_BY
fields SPFLI~CONNID, SPFLI~CARRID, SCARR~CARRNAME into table @data(LT_FLIGHTS_DE).
from SCARR where CARRID eq @LK_CARRIER_FA
into @data(L_CARRIER_EXISTS).
Existence check
select single @ABAP_TRUEfrom SCARR where CARRID eq @LK_CARRIER_FA
into @data(L_CARRIER_EXISTS).
Select with a Package
SELECT * FROM MARA INTO TABLE IT_MARA PACKAGE SIZE 100 UP TO 1000 ROWS
WHERE MTART = P_MTART .
IF SY-SUBRC = 0.
APPEND LINES OF IT_MARA TO IT_MARA1. "append data to another internal table
ENDIF.
ENDSELECT.
SELECT - FROM @itab
DATA result1 LIKE itab.
SELECT table_line
FROM @itab AS numbers
INTO TABLE @result1.
cl_demo_output=>write( result1 ).
Case
select from SBOOK fields CARRID, CONNID,
case SMOKER
when 'X' then 'SMOKER'
else 'NON SMOKER'
end as SMOKER
into table @data(LT_SMOKER).
cast( CARRID as char( 20 ) ) as CARRID_TEXT,
cast( CONNID as char( 4 ) ) as CONNID_TEXT
into table @data(LT_SPFLI_CAST).
Casting
select from SPFLI fieldscast( CARRID as char( 20 ) ) as CARRID_TEXT,
cast( CONNID as char( 4 ) ) as CONNID_TEXT
into table @data(LT_SPFLI_CAST).
Arithmetics
select CARRID, CONNID,
cast( SEATSMAX as fltp ) / cast( 2 as fltp ) as SEATS
from SFLIGHT into table @data(LT_FLIGHT).
Union
DATA prog_range TYPE RANGE OF trdir-name.
SELECT 'I' AS sign, 'EQ' AS option, obj_name AS low, ' ' AS high
FROM tadir
WHERE pgmid = 'R3TR' AND object = 'PROG' AND devclass = @devclass
UNION
SELECT 'I' AS sign, 'CP' AS option, obj_name && '*' AS low, ' ' AS high
FROM tadir
WHERE pgmid = 'R3TR' AND object = 'CLAS' AND devclass = @devclass
*Not perfect, from 7.51 on the following is better
*SELECT 'I' AS sign, 'CP' AS option, concat( rpad( obj_name, 30, '=' ) , '*' ) AS low,
* ' ' AS high
* FROM tadir
* WHERE pgmid = 'R3TR' AND object = 'CLAS' AND devclass = @devclass
UNION
SELECT 'I' AS sign, 'EQ' AS option, 'SAPL' && obj_name AS low, ' ' AS high
FROM tadir
WHERE pgmid = 'R3TR' AND object = 'FUGR' AND devclass = @devclass
UNION
SELECT 'I' AS sign, 'CP' AS option, 'L' && obj_name && '+++' AS low, ' ' AS high
FROM tadir
WHERE pgmid = 'R3TR' AND object = 'FUGR' AND devclass = @devclass
INTO TABLE @prog_range.
Comments
Post a Comment