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 ?
Related
If I use the internal table as a standard table the parameter T_TABLE accepts normally,
but when I declare as a sorted table this error happen: "ITUSER" is not type-compatible with formal parameter "T_TABLE".
Can you guys help me identify why this happens?
TABLES: USER_ADDR,USR41.
TYPES: BEGIN OF LINE02_TYPE,
Z_BNAME TYPE USER_ADDR-BNAME,
Z_NAME TYPE USER_ADDR-NAME_FIRST,
Z_LAST TYPE USER_ADDR-NAME_LAST,
Z_TERMI TYPE USR41-TERMINAL,
Z_LASTD TYPE USR41-LOGON_DATE,
Z_KOSTL TYPE USER_ADDR-KOSTL,
END OF LINE02_TYPE.
DATA: ITUSER TYPE SORTED TABLE OF LINE02_TYPE WITH UNIQUE KEY Z_BNAME,
"ITUSER TYPE standard TABLE OF line02_type,
R_TABLE TYPE REF TO CL_SALV_TABLE.
START-OF-SELECTION.
SELECT A~BNAME A~NAME_FIRST A~NAME_LAST B~TERMINAL B~LOGON_DATE A~KOSTL FROM USER_ADDR AS A
LEFT JOIN USR41 AS B ON B~BNAME = A~BNAME
INTO TABLE ITUSER.
CALL METHOD CL_SALV_TABLE=>FACTORY
IMPORTING
R_SALV_TABLE = R_TABLE
CHANGING
T_TABLE = ITUSER.
CALL METHOD R_TABLE->DISPLAY.
Unfortunately I do not see it is documented anywhere but T_TABLE has to be a STANDARD TABLE. If you dig deeper into FACTORY method the T_TABLE parameter is passed to SET_DATA method which requires the table as STANDARD TABLE
try.
r_salv_table->set_data(
changing
t_table = t_table ).
catch cx_salv_no_new_data_allowed. "#EC NO_HANDLER
endtry.
Moreover if you define the parameter as TABLE a STANDARD TABLE is implicitely meant. Here is the reference
As already pointed out, the signature for factory is changing t_table type table.
TABLE is a generic ABAP type which is the old way to say STANDARD TABLE.
This is required since actions you perform via the ALV are reflected on the table itself. When you press the sort button the internal table will also be sorted (hence also changing). So when you get e.g. the double click event, you can safely access my_table[ row ] since it's sorted in the same way as it's displayed to the user.
Such a sorting cannot be represented in HASHED TABLE or SORTED TABLE (sort can be on any column/s).
I have the following code:
SELECT S~CLUSTD AS ZZCLUSTD
INTO CORRESPONDING FIELDS OF TABLE #lt_viqmel_iflos
FROM viqmel AS v
LEFT OUTER JOIN stxl AS S
ON s~tdobject = #lv_qmel
AND s~tdname = v~qmnum
Select statement generates following short dump:
Only the prefixed length field can be used to read from the LRAW field or
LCHR field S~CLUSTD.
Internal table lt_viqmel_iflos is type viqmel_iflos(DB view which contains DB table QMEL) to which I appended ZZCLUSTD type char200.
The problem is that I cannot make ZZCLUSTD type LRAW in QMEL because I get the following error:
So my only option (that I am aware of) remains to select into char200 the first 200 characters of LRAW.
Is this even possible?
Or is there another way to select LRAW data?
I found the info about the topic, but unfortunately I can't adapt it to my scenario:read LRAW data
In fact, there are two questions here.
The first one is the activation error of table QMEL:
Field ZZCLUSTD does not have a preceding length field of type INT4
A DDIC table containing a column of type LCHR and LRAW, requires that it's always immediately preceded with a column of type INT2 or INT4 (although the message says only INT4).
The second question is about how to read such a field. Both columns must always be read at the same time, and the INT2/INT4 column must be "read before" the LCHR/LRAW column. The only reference I could find to explain this restriction is in the note 302788 - LCHR/LRAW fields in logical cluster tables.
The INT2 column of STXL table being named CLUSTR, the following code works:
TYPES: BEGIN OF ty_viqmel_iflos,
clustr TYPE stxl-clustr, "INT2
zzclustd TYPE stxl-clustd, "LCHR
END OF ty_viqmel_iflos.
DATA lt_viqmel_iflos TYPE TABLE OF ty_viqmel_iflos.
SELECT S~CLUSTR, S~CLUSTD AS ZZCLUSTD
INTO CORRESPONDING FIELDS OF TABLE #lt_viqmel_iflos
FROM viqmel AS v
INNER JOIN stxl AS S
ON s~tdname = v~qmnum
UP TO 100 ROWS.
NB: there is a confusion in your question, where you refer to both CLUSTD from STXL and ZZCLUSTD from QMEL. I don't understand what you are trying to achieve exactly.
NB: if you want to read the texts from the table STXL, there's another solution by calling the function module READ_TEXT_TABLE, or READ_MULTIPLE_TEXTS if you prefer. They were made available by the note 2261311. In case you don't have or can't install these function modules, you may try this gist which does the same thing. It also contains a reference to another discussion.
NB: for information, to be more precise, LRAW contains bytes, not characters, and for data clusters (case of STXL), these bytes correspond to any values (characters in the case of STXL) zipped with the statement EXPORT and are to be unzipped with IMPORT`.
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.
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>.
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.