Given a field symbol pointing to a table, how do I assign it to a structure field symbol? - abap

I have a field symbol <lt_something> which points to a table. This table has a single line as content. How do I assign that line in the table to a new field symbol, say <ls_something> ?
I tried :
READ TABLE <lt_something> INDEX 1 REFERENCE INTO <ls_something>.
But the above code dumps, what is the right approach to this problem?

You're mixing up field symbols and data references. Use either
DATA lr_foo TYPE REF TO something.
READ TABLE lt_something INDEX 1 REFERENCE INTO lr_foo.
lr_foo->bar = 'baz'.
or
FIELD-SYMBOLS <ls_foo> TYPE something.
READ TABLE lt_something INDEX 1 ASSIGNING <ls_foo>.
<ls_foo>-bar = 'baz'.
but do not merge the two ways.

Are you sure that your field symbol table is assigned before you perform your read? The general structure of your read table looks OK, but you should use ASSIGNING in your read statement:
FIELD-SYMBOLS: <lt_something> TYPE ANY TABLE,
<ls_something> TYPE ANY.
" ... blah blah presume you did some work on <lt_something>...
IF <lt_something> IS ASSIGNED.
READ TABLE <lt_something> INDEX 1 ASSIGNING <ls_something>.
" Check sy-subrc or <ls_something> IS ASSIGNED.
" Do work.
ENDIF.

The field-symbol needs to be the line type of the table, not the table itself.
DATA:
i_vbak type standard table of vbak.
FIELD-SYMBOLS:
<i_vbak> type vbak.
READ TABLE i_vbak ASSIGNING <i_vbak>.

Related

A short syntax to fill ABAP table from another table?

In the old ABAP syntax I have to loop over the source table, and inside of the loop append value to the table.
For example:
DATA:
it_source_table type table of mara,
et_result_table type table of matnr.
loop at it_source_table into data(ls_source_table).
append ls_source_table-matnr to et_result_table.
endloop.
Is there with a new ABAP syntax (750, 752) ("move-corresponding", "value#") a way to achieve the same in less sentences?
You can use the VALUE operator with the FOR ... IN addition:
et_result_table = VALUE #( FOR material IN it_source_table ( material-matnr ) ).

Assigning Inline declared table to Field Symbols

There is an inline-declared table generated from a SELECT statement as below:
SELECT *
INTO TABLE #DATA(lt_result)
FROM scarr.
How can lt_result be assigned to a Field Symbol?
I tried the following way:
FIELD-SYMBOLS: <fs_lt_result> TYPE ANY.
LOOP AT lt_result ASSIGNING <fs_lt_result>.
But I'm not able to call any components in the field symbol inside the loop like:
WRITE / <fs_lt_result>-carrid.
(syntax error: The data object "<FS_LT_RESULT>" does not have a structure and therefore does not have a component called "CARRID".)
Inline declaration of a field symbol for an internal table in an ASSIGN statement and inline declaration of a field symbol for the rows of the table in a LOOP.
LOOP AT <lt_result> ASSIGNING FIELD-SYMBOL(<line>).
...
ENDLOOP.
source: https://help.sap.com/doc/abapdocu_750_index_htm/7.50/en-US/abenfield-symbol_inline.htm
You declared that the field symbol is of unknown type (ANY, i.e. the exact type is known only at run time), so the compiler can't be sure that the mentioned component (CARRID) exists, hence the syntax error.
If you want to mention a component statically, the compiler must be informed of the exact type (what components exist).
For instance, this would work:
SELECT *
INTO TABLE #DATA(lt_result)
FROM scarr.
FIELD-SYMBOLS: <fs_lt_result> TYPE scarr.
LOOP AT lt_result ASSIGNING <fs_lt_result>.
WRITE / <fs_lt_result>-carrid.
ENDLOOP.
Or use the inline declaration of the field symbol as you proposed in your own answer/solution:
LOOP AT lt_result ASSIGNING FIELD-SYMBOL(<fs_lt_result>).
NB: if your internal table had a type decided at run time only, it would be impossible to indicate statically the component name and you'd need to refer to the component dynamically:
ASSIGN COMPONENT ('CARRID') OF STRUCTURE <fs_lt_result> TO FIELD-SYMBOL(<field>).
IF sy-subrc = 0.
WRITE / <field>.
ENDIF.
EXAMPLE:
FIELD SYMBOL : TYPE VBAK.
DATA: ITAB TYPE TABLE OF VBAK .
SELECT * FROM VBAK INTO TABLE ITAB UP TO 10 ROWS.
LOOP AT ITAB ASSIGNING
WRITE : / -VBELN . (WRITE WHICH OUTPUT YOU WANT TO DISPLAY)
ENDLOOP.

Concatenating strings in SAP Query

I'm creating a BAPI for SAP R/3. The equivalent in MSSQL of what I'm trying to write is this:
select
bkpf.BELNR,
bkpf.BUKRS,
bkpf.GJAHR,
bkpf.AWKEY
into
#tab
from
bkpf
where
exists ( select 1 from #n_tab n where CONCAT(n.BELNR, n.GJAHR) = bkpf.AWKEY )
;
But apparently Open Sql doesn't allow operations in queries. So for what I researched, the table I want to "join" must be retrieved to an in memory table, create a new column in a loop doing the operation and the compare with the #tab table. But I'm struggling with the syntax. What I have so far is something like this:
FUNCTION ZBAPI_TEST.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"----------------------------------------------------------------------
select
bkpf~BELNR
bkpf~BUKRS
bkpf~GJAHR
bkpf~AWKEY
into ITAB_BKPF
from bkpf.
loop at ITAB_BKPF.
ITAB_BKPF-chkey = CONCATENATE BELNR GJAHR.
modify ITAB_BKPF.
endloop.
ENDFUNCTION.
But I'm getting the following errors.
Field "ITAB_BKPF" is unknown. It is neither in one of the
specified tables nor defined by a "DATA" statement.
Field "ITAB_BKPF-GJAHR" is unknown. It is neither in one of
the specified tables nor defined by a "DATA" statement.
Incorrect nesting: Before the statement "ENDFUNCTION", the
control structure introduced by "SELECT" must be concluded with
"ENDSELECT".
Incorrect nesting: Before the statement
"+END-OF-INCLUDE", the control structure introduced by "FUNCTION" must
be concluded with "ENDFUNCTION".
There's clearly an open statement. But I'm not really familiar with the language and don't know where the period is required or if I misplaced any closing statement. Other approach I saw online was with a construct SELECT..ENDSELECT:
FUNCTION ZBAPI_TEST.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"----------------------------------------------------------------------
select
bkpf~belnr
bkpf~bukrs
bkpf~gjahr
bkpf~awkey
into corresponding fields
wa_bkpf
from bkpf.
wa_bkpf-chkey = concatenate belnr gjahr.
append wa_bkpf to itab_bkpf.
endselect.
ENDFUNCTION.
But this generate a new batch of errors:
Field "CORRESPONDING" is unknown. It is neither in one of the
specified tables nor defined by a "DATA" statement. Field
"WA_BKPF-CHKEY" is unknown. It is neither in one of the
specified tables nor defined by a "DATA" statement. Field "WA_BKPF"
is unknown. It is neither in one of the specified tables nor defined
by a "DATA" statement.
I suspect the solutions and examples I found online skip some part where they define some of the structures they use. But I don't really know how to do it. Can someone help?
EDIT:
The final code looks like that:
types: begin of t_bkpf,
belnr type belnr_d,
bukrs type bukrs,
gjahr type gjahr,
awkey type awkey,
chkey type string.
types: end of t_bkpf.
data: itab_bkpf type standard table of t_bkpf.
field-symbols: <wa> type t_bkpf.
select
BELNR
BUKRS
GJAHR
AWKEY
into corresponding fields of table ITAB_BKPF
from bkpf.
loop at ITAB_BKPF assigning <wa>.
CONCATENATE <wa>-BELNR <wa>-GJAHR into <wa>-chkey.
endloop.
manoftheyear.
Let me know if this works for you:
*** Definition of custom type.
TYPES: BEGIN OF ty_bkpf,
belnr TYPE bukrs,
bukrs TYPE belnr_d,
gjahr TYPE gjahr,
awkey TYPE awkey,
chkey TYPE string, " Replace with the type you need to use.
END OF ty_bkpf.
*** Definition of internal table of custom type.
DATA lt_bkpf TYPE STANDARD TABLE OF ty_bkpf.
*** Definition of field symbol (pointer) of custom type.
FIELD-SYMBOLS <lfs_bkpf> TYPE ty_bkpf.
*** Extraction of data from BKPF database table.
SELECT belnr bukrs gjahr awkey
FROM bkpf
INTO CORRESPONDING FIELDS OF TABLE lt_bkpf.
*** Checks if extraction was succesful.
IF sy-subrc IS INITIAL.
UNASSIGN <lfs_bkpf>.
*** Loop internal table...
LOOP AT lt_bkpf ASSIGNING <lfs_bkpf>.
*** ...and create value for field CHKEY.
CONCATENATE <lfs_bkpf>-belnr
<lfs_bkpf>-gjahr
INTO <lfs_bkpf>-chkey. " By using a pointer, there's no need to use MODIFY sentence.
ENDLOOP.
ENDIF.
Cheers.
You can use CONCAT directly on QUERY. Example:
SELECT SINGLE CONCAT( CONCAT( a~name1, a~name2 ), a~name3 )
FROM ( ( adrc AS a
INNER JOIN j_1bbranch AS j ON a~addrnumber = j~adrnr )
INNER JOIN t001w AS t ON j~cgc_branch = t~j_1bbranch
AND j~bukrs = #i_postab-bukrs
AND t~werks = #e_postab-prctr+4(4) )
INTO #e_postab-company_name.

Aggregating rows

In sap I have a table, there are rows with the same name but with different quantity.
I want to summarize this rows like this:
SELECT c~aufnr
p~matnr p~bdter p~meins p~baugr p~dbskz p~erfmg p~aufnr
f~maktx
INTO CORRESPONDING FIELDS OF TABLE it_tab
FROM afpo AS c
INNER JOIN resb AS p ON c~aufnr = p~aufnr
INNER JOIN makt AS f ON p~matnr = f~matnr.
LOOP AT it_tab INTO fs_tab.
COLLECT fs_tab INTO it_tab_collected.
ENDLOOP.
it_tab = it_tab_collected.
But in this case it sums only absolutelly identically rows. I need to sum up rows only with the same name.
How can I achieve this?
Regards,
Alexander.
As icbytes already said, COLLECT uses the key fields to determine which fields to aggregate. I would suggest defining some data types to match your scenarion:
TYPES: BEGIN OF t_my_type,
key_a TYPE foo,
key_b TYPE foo,
nokey_c TYPE foo,
nokey_d TYPE foo,
END OF t_my_type,
tt_my_type_list TYPE STANDARD TABLE OF t_my_type WITH DEFAULT KEY,
tt_my_type_hash TYPE HASHED TABLE OF t_my_type WITH KEY key_a key_b.
DATA: lt_result TYPE tt_my_type_list,
lt_sums TYPE tt_my_type_hash.
FIELD-SYMBOLS: <ls_result> TYPE t_my_type.
LOOP AT lt_result ASSIGNING <ls_result>.
COLLECT <ls_result> INTO lt_sums.
ENDLOOP.
...or you might want to use an aggregate function in the first place...
I'd stick away from COLLECT, even tho you can save a few lines of code, there's no need to use it and adds restrains for no reason.
I've had problems to maintain/add features to previously written code that used COLLECT because it doesn't allow non-key non-numeric fields to be added to the internal tables and sometimes I've been forced to modify a lot of COLLECT sentences or introduce unnecessary complexity to the code (like using extra internal tables).
Example (taking vwegert's example as a base, who did a good job explaining how to use COLLECT correctly):
TYPES: BEGIN OF t_my_type,
key_a TYPE foo,
key_b TYPE foo,
""" with COLLECT all 'foo' types **has to be** numeric
nokey_c TYPE foo,
nokey_d TYPE foo,
""" if you ever need to add something like a note field to the table
""" you'll find out that you have to change all the COLLECT sentences
""" because this new field wouldn't let the code compile
nokey_notes(50) type c,
END OF t_my_type,
tt_my_type_list TYPE STANDARD TABLE OF t_my_type WITH DEFAULT KEY,
tt_my_type_hash TYPE HASHED TABLE OF t_my_type WITH KEY key_a key_b.
DATA: lt_result TYPE tt_my_type_list,
lt_sums TYPE tt_my_type_hash.
FIELD-SYMBOLS: <ls_result> TYPE t_my_type.
""" just imagine you need the 'nokey_notes' field for an ALV for user input.
PERFORM show_alv USING lt_result.
""" and you don't care about this new field when doing
""" the sum/average or whatever you are trying to calculate
""" well, it won't work with COLLECT...
LOOP AT lt_result ASSIGNING <ls_result>.
COLLECT <ls_result> INTO lt_sums.
ENDLOOP.
My recommendation would be using AT... ENDAT (besides SELECT's aggregate functions, which is a good option, specially if you don't need the individual data).
Of course the code will gain a few extra lines, but in my opinion/experience they are worth it, because the code will be easier to maintain in the future.
TYPES: BEGIN OF t_my_type,
key_a TYPE foo,
key_b TYPE foo,
""" with COLLECT all 'foo' types **has to be** numeric
nokey_c TYPE foo,
nokey_d TYPE foo,
""" without COLLECT you are able to add new non-key non-numeric fields
nokey_notes(50) type c,
END OF t_my_type,
tt_my_type_list TYPE STANDARD TABLE OF t_my_type.
DATA: lt_result TYPE tt_my_type_list,
lt_sum TYPE tt_my_type_list.
lwa_sum TYPE t_my_type.
FIELD-SYMBOLS: <ls_result> TYPE t_my_type.
""" just imagine you need the 'nokey_notes' field for an ALV for user input.
PERFORM show_alv USING lt_result.
""" sorting is important when using AT... ENDAT, there are other gotchas too
""" make sure you read its documentation carefully if you never used it
""" (like everything right? :P)
SORT lt_result BY key_a key_b.
REFRESH lt_sum.
""" and you don't care about 'nokey_notes' field when doing calculation
LOOP AT lt_result ASSIGNING <ls_result>.
AT NEW key_b.
""" this get executed when the work area's primary keys
""" change, good time to prepare the lwa_sum work area.
CLEAR lwa_sum.
lwa_sum-key_a = lwa_sum-key_a.
lwa_sum-key_b = lwa_sum-key_b.
ENDAT.
""" do whatever math/logic is need with the fields
lwa_sum-nokey_c = lwa_sum-nokey_c + <ls_result>-nokey_c.
lwa_sum-nokey_d = lwa_sum-nokey_d + <ls_result>-nokey_d.
AT END OF key_b.
""" this get executed when the work area's primary keys
""" is about to change (in next iteration) or at the last
""" record of the table
""" good place to save the results to a new internal table
APPEND lwa_sum to lt_sums.
ENDAT.
ENDLOOP.
AFAIK collect uses keys as it can decide which rows shall create an aggregate.
If You have other character-values prepending numerical values, delete them in another itab, so that the only c-like column, which is filled, will be the "name". This will serve as the only key to let abap processor aggregate.
Did that help ?

How to set dynamic key for READ TABLE?

I'm trying to work out a way to read an internal table that has to be created dynamically. I have created the following report that fills a dynamic internal table with data.
On the last line, I'm trying to read it with a key (mandt for example), but I I get this syntax error:
The specified type has no structure and therefore no component called MANDT
I have debugged and I can see that <any_tab> has been populated successfully and the structure of the table (field names) are correct. The problem presents itself when I try to read the table into a work area. Maybe I'm doing this wrong, but it seems like something that should be possible to do, and I have a feeling I'm missing something small.
The reason I am trying this out is that I have found identical selects happening in a program and want to buffer records in memory and read them from there to avoid DB accesses. This is easy to implement, however I haven't done this when the table, where clause and into clause of the OPEN SQL statement I'm trying to optimize are dynamic.
How to correct the syntax error?
DATA: t681_rep TYPE TABLE OF t681 , wa_681 LIKE LINE OF t681_rep,
tabref TYPE REF TO data , waref TYPE REF TO data.
FIELD-SYMBOLS: <any_tab> TYPE ANY TABLE,
<any_wa> TYPE ANY,
<var1> TYPE ANY.
"fill t681_rep
SELECT *
FROM t681
INTO TABLE t681_rep
UP TO 1 ROWS WHERE kotab = 'A002'.
READ TABLE t681_rep INTO wa_681 WITH KEY kotab = 'A002'.
IF sy-subrc = 0.
"if A002 is found create a table of that type and fill it
CREATE DATA tabref TYPE TABLE OF (wa_681-kotab).
ASSIGN tabref->* TO <any_tab>.
SELECT * UP TO 10 ROWS
FROM (wa_681-kotab)
INTO TABLE <any_tab>.
ENDIF.
CREATE DATA waref TYPE a002.
ASSIGN waref->* TO <any_wa>.
READ TABLE <any_tab> ASSIGNING <any_wa> WITH KEY mandt = '800'. <- problem area
IF sy-subrc = 0.
"do stuff with <any_wa>...
ENDIF.
You just need to put the field name in parentheses.
data: field type string.
field = 'MANDT'.
READ TABLE <any_tab> ASSIGNING <any_wa> WITH KEY (field) = '800'.
IF sy-subrc = 0.
"do stuff with <any_wa>...
ENDIF.
AFAIK, you have to do it the 'long way round':
FIELD-SYMBOLS: <any_field> TYPE any.
LOOP AT <any_tab> ASSIGNING <any_wa>.
ASSIGN COMPONENT 'MANDT' OF STRUCTURE <any_wa> TO <any_field>.
IF <any_field> <> 800.
CONTINUE.
ENDIF.
" do stuff with <any_wa> - you will have to assign <any_field> again to access fields.
ENDLOOP.
You are trying to beat a database in efficiency, it is a loosing battle.
Just go to SE11, select your table, go to technical settings and change the technical settings ( buffering & buffering type ), you do not require an object modification key for this. You can also make sure that the size category is correct.
You can use RTTS to get the table keys.
data table_name type string.
table_name = 'A002'.
" Dynamically create the table type
data the_table type ref to data.
create data the_table type table of (table_name).
" Use RTTS to get table keys
data typedescription type ref to cl_abap_tabledescr.
typedescription ?= cl_abap_tabledescr=>describe_by_data_ref( the_table ).
data keys type abap_table_keydescr_tab.
keys = typedescription->get_keys( ).
REPORT y_test_dynamic_table.
DATA: table_name TYPE string,
typedescription TYPE REF TO cl_abap_tabledescr,
keys TYPE abap_keydescr_tab,
ls_key TYPE abap_keyname.
table_name = 'ZYFRM_STG'.
" Dynamically create the table type
DATA the_table TYPE REF TO data.
CREATE DATA the_table TYPE TABLE OF (table_name).
" Use RTTS to get table keys
typedescription ?= cl_abap_tabledescr=>describe_by_data_ref( the_table ).
keys = typedescription->KEY.
loop at keys INTO ls_key.
***
ENDLOOP.