How can I write the below code
MODIFY git_oi_tab FROM VALUE #( dmbtr = dmbtr * -1 )
TRANSPORTING dmbtr
WHERE shkzg = 'H'.
The DMBTR in dmbtr * -1 is unknown.
Thanks
Elias
PS. I found this but is not what I want as returns the calculated field but clear all others in ITAB it_final_data
it_final_data = VALUE #( LET lit_final_data = it_final_data IN FOR lwa_final_data IN lit_final_data
( ovh_ifrs_diff_value = lwa_final_data-ovh_ifrs_value * lv_1st_ledger_amount / sum_OVH_IFRS ) ).
It appears like you want to invert the sign of the field DMBTR in all rows where the field SHKZG is equal to 'H'
In that case you won't get around doing it with a loop:
LOOP AT git_oi_tab ASSIGNING FIELD-SYMBOL(<ls_oi_tab>) WHERE shkzg = 'H'.
<ls_oi_tab>-dmbtr = <ls_oi_tab>-dmbtr * -1.
ENDLOOP.
If you use the MODIFY itab FROM wa variant, then wa is not constructed separately for each line. It is taken once as-is and applied to all lines. You can't sneak any dynamic logic in there.
Related
Let's say I have a table quants and want to find out if any line exists, where the field lenum is not initial. The table is declared inline using a select statement, so I do not have a key available.
Because I don't have a key, the following solution does not work:
line_exists( VALUE #( FOR wa IN quants WHERE ( lenum IS NOT INITIAL ) ( wa ) ) )
Since I want to check for inequality, a table expression does not work:
line_exists( quants[ lenum NE '' ] )
The only solution that I have come up with so far is the following:
abap_true EQ REDUCE abap_bool( INIT bool = abap_false FOR quant IN quants WHERE ( lenum IS NOT INITIAL ) NEXT bool = abap_true )
Obviously there are "old fashioned" solutions, but is there any newer-style?
By "old fashioned" I mean solutions like this:
LOOP AT quants INTO DATA(wa).
IF wa-lenum IS INITIAL.
DATA(found) = abap_true.
ENDIF.
ENDLOOP.
IF found EQ abap_true.
...
ENDIF.
The only thin in "new fashion" would be SELECT, FROM #itab.
DATA(lv_exists) = abap_false.
SELECT SINGLE #abap_true FROM #lt_quant AS quant WHERE quant~lenum IS NOT INITIAL INTO #lv_exists.
See documentation link for performance impact (best case is handled like a table in the table buffer) and limitations (e.g. no string column).
The most performant and less restrictions would be this:
LOOP AT lt_quant TRANSPORTING NO FIELDS WHERE lenum IS NOT INITIAL.
EXIT.
ENDLOOP.
DATA(lv_exists) = xsdbool( sy-subrc = 0 ).
Is it possible to mix in a FOR with CONCATENATE along with BASE statement?
Normally, itab1 = VALUE #( BASE itab1 ( value1 ) ) will append line1 into itab1 without overwriting. Shouldn't it be the same when using FOR along with BASE?
This is my thought process, but I get the No component exists with the name "FOR" error:
itab1 =
VALUE #(
BASE itab1
( value1 && value2 )
( VALUE #(
FOR line in itab2
( line-fld1 && line-fld2 ) )
).
Shouldn't it be the same when using FOR along with BASE
Yes, the semantics is the same. You use BASE for preserving the rows of the LHS when adding rows of RHS, and line specification is the same:
itab1 = VALUE #( BASE itab1 FOR line in itab2 ( matnr = line-matnr
maktx = line-matnr && line-spras
spras = line-spras ) ).
However, there is a nuance you should remember here: you cannot put the same itab to the inline-declared LHS, to the BASE source and to the FOR loop, it will give you infinite loop and TSV_TNEW_PAGE_ALLOC_FAILED dump.
I have tried out what Sandra had suggested in the comments and it worked like a charm:
You may append the lines of an internal table only by using ( LINES OF
itab ), so in your case it should be ( LINES OF VALUE #( ... ) ) –
Sandra Rossi
itab1 =
VALUE #( BASE itab1 ( value1 && value2 )
( LINES OF VALUE #( FOR line in itab2 ( line-fld1 && line-fld2 ) ) ).
Is there a way to get a list of all BUKRS which the current user is allowed to see?
I want to use this list as a filter in open sql. Imagine the result of the method I search stored the result in bk_list. Then I could use bk_list like this:
SELECT * FROM some_table WHERE bukrs IN bk_list
Another way to do it, based on the class CL_AUTH_OBJECTS_TO_SQL (officially supported, as of ABAP 7.50, as explained in the documentation of AUTHORITY-CHECK), here the program reads the flights from the read-authorized airline carriers :
DATA(authsql) = cl_auth_objects_to_sql=>create_for_open_sql( ).
authsql->add_authorization_object( EXPORTING
iv_authorization_object = 'S_CARRID'
it_activities = VALUE #( ( auth_field = 'ACTVT' value = '03' ) )
it_field_mapping = VALUE #(
( auth_field = 'CARRID'
view_field = VALUE #( table_ddic_name = 'SFLIGHT' field_name = 'CARRID' ) ) ) ).
DATA(where) = authsql->get_sql_condition( ).
SELECT * FROM sflight INTO TABLE #data(sflights) WHERE (where).
I am afraid you can do it one by one only. Roughly:
SELECT bukrs
INTO TABLE #DATA(lt_t001)
FROM t001
WHERE ... . "Selection critera, if necessary
LOOP AT lt_t001
ASSIGNING FIELD-SYMBOL(<ls_t001>).
DATA(lv_tabix) = sy-tabix.
AUTHORITY-CHECK OBJECT 'F_BKPF_BUK'
ID 'BUKRS' FIELD <ls_t001>-bukrs
ID 'ACTVT' FIELD '03'. "Here you need the proper activity (display '03' /change '02' / etc.)
IF sy-subrc <> 0. "Auth check failed
DELETE lt_t001 INDEX lv_tabix.
ENDIF.
ENDLOOP.
At the end lt_t001 contains only the company codes, for which the user has authorization.
How can I check the repetitive value in the "Form #" column?
I want to highlight it later as duplicate record.
LOOP AT ZVBELNEXTTAB WHERE werks IN werks.
ZVBELNEXTTAB_COPY-WERKS = ZVBELNEXTTAB-WERKS.
ZVBELNEXTTAB_COPY-MANDT = ZVBELNEXTTAB-MANDT.
ZVBELNEXTTAB_COPY-BUKRS = ZVBELNEXTTAB-BUKRS.
ZVBELNEXTTAB_COPY-VBELN = ZVBELNEXTTAB-VBELN.
ZVBELNEXTTAB_COPY-EVBELN = ZVBELNEXTTAB-EVBELN.
ZVBELNEXTTAB_COPY-FKDAT = ZVBELNEXTTAB-FKDAT.
ZVBELNEXTTAB_COPY-VBLSTAT = ZVBELNEXTTAB-VBLSTAT.
ZVBELNEXTTAB_COPY-ZPRN = ZVBELNEXTTAB-ZPRN.
ZVBELNEXTTAB_COPY-UNAME = ZVBELNEXTTAB-UNAME.
ZVBELNEXTTAB_COPY-TYPE = ZVBELNEXTTAB-TYPE.
curr = ZVBELNEXTTAB-EVBELN.
lv_tab = SY-TABIX + 1.
READ TABLE ZVBELNEXTTAB INDEX lv_tab.
next = ZVBELNEXTTAB-EVBELN.
IF curr GT next.
a = curr - next.
ELSE.
a = next - curr.
ENDIF.
IF a GT 1.
curr = curr + 1.
next = next - 1.
ZVBELNEXTTAB_COPY-MISSINGFROM = curr.
ZVBELNEXTTAB_COPY-MISSINGTO = next.
ELSE.
ZVBELNEXTTAB_COPY-MISSINGFROM = ''.
ZVBELNEXTTAB_COPY-MISSINGTO = ''.
ENDIF.
APPEND ZVBELNEXTTAB_COPY.
SORT ZVBELNEXTTAB_COPY BY EVBELN.
ENDLOOP.
ENDFORM.
I still trying to check the duplicate "Form #" column by using 1 dimensional array by looping them.
Use GROUP BY functionality during looping. You wanna extract duplicates based on comparison fields Company code, Plant, Form #, Sales Doc, Billing date, Username.
So you should write something like this:
TYPES tt_vbeln TYPE STANDARD TABLE OF vbeln WITH DEFAULT KEY.
DATA duplicates TYPE tt_vbeln.
LOOP AT ZVBELNEXTTAB INTO DATA(zvbeln)
GROUP BY ( BUKRS = zvbeln-BUKRS
WERKS = zvbeln-WERKS
VBELN = zvbeln-VBELN
EVBELN = zvbeln-EVBELN
FKDAT = zvbeln-FKDAT
UNAME = zvbeln-UNAME
size = GROUP SIZE )
ASCENDING REFERENCE INTO DATA(group_ref).
CHECK group_ref->*-size > 1. "extracting dups
duplicates = VALUE tt_vbeln( BASE duplicates FOR <form_num> IN GROUP group_ref ( <form_num> ) ).
* setting color
MODIFY duplicates FROM VALUE tt_vbeln( line_color = 'C410' ) TRANSPORTING line_color WHERE line_color IS INITIAL.
ENDLOOP.
That allows you to extract sets of duplicated values like this
By the way, in the above sample rows of blue dataset differ in fields Form # and Username, so my GROUP snippet won't actually work on them. You should adjust grouping fields accordingly, for example leave only VBELN field as grouping field.
Beforehand you should add field line_color to your structure where you will put color codes for duplicates datasets.
Good sample of conditional coloring an ALV resides here.
I get a internal table from a Function Module call that returns ~ 100 rows. About 40% of the rows are not relevant to me because I only need the entries with PAR1 = "XYZ".
On SQL tables (transparent tables), I can use a
select count(*) from tab where PAR1 = "XYZ"
to get the number of valid entries.
Looking at the documentation, all I could find was the READ Table syntax to iterate through the table. My current approach is to basically have a loop and increase if the row contains the value I want. But this seems very inefficient.
Is there a better approach for my requirement?
As from 740 SP05 you can use:
DATA(lv_lines) = REDUCE i( INIT x = 0 FOR wa IN gt_itab
WHERE( F1 = 'XYZ' ) NEXT x = x + 1 ).
for counting the number of lines in gt_itab meeting codntion f1 = 'xyz'.
Do whatever feels right to you. With ~100 rows, virtually nothing will make a huge difference in runtime. For me, stability would be more important than speed in this case.
That being said, you could try this:
LOOP AT lt_my_table TRANSPORTING NO FIELDS WHERE par1 = 'XYZ'.
ADD 1 TO l_my_counter.
ENDLOOP.
If the entries in the internal table are irrelevant you could do something like this.
DELETE lt_table WHERE par1 <> 'XYZ'.
Then you can count the remaining relevant records by using lines( lt_table ) or DESCRIBE TABLE lt_table LINES l_number_of_lines.
Here is an example.
TYPES: BEGIN OF tt_test,
par1 TYPE c LENGTH 3,
END OF tt_test.
DATA: lt_table TYPE TABLE OF tt_test.
DATA: l_number_of_lines TYPE i.
FIELD-SYMBOLS: <fs_par1> LIKE LINE OF lt_table.
APPEND INITIAL LINE TO lt_table ASSIGNING <fs_par1>.
<fs_par1>-par1 = 'XYZ'.
APPEND INITIAL LINE TO lt_table ASSIGNING <fs_par1>.
<fs_par1>-par1 = 'ABC'.
APPEND INITIAL LINE TO lt_table ASSIGNING <fs_par1>.
<fs_par1>-par1 = 'XYY'.
APPEND INITIAL LINE TO lt_table ASSIGNING <fs_par1>.
<fs_par1>-par1 = 'XYZ'.
APPEND INITIAL LINE TO lt_table ASSIGNING <fs_par1>.
<fs_par1>-par1 = 'XYZ'.
l_number_of_lines = LINES( lt_table ).
WRITE / l_number_of_lines.
DESCRIBE TABLE lt_table LINES l_number_of_lines.
WRITE / l_number_of_lines.
DELETE lt_table WHERE par1 <> 'XYZ'.
l_number_of_lines = LINES( lt_table ).
WRITE / l_number_of_lines.
Variant with FOR should also work, however it requires declared table type of that table:
TYPES: tt_mara TYPE TABLE OF mara WITH EMPTY KEY.
DATA(count) = lines( VALUE tt_mara( FOR line IN lt_mara WHERE ( matnr = 'XXX' ) ( line ) ) ).