How to check if the field TFK is maintained in the table - abap

I am trying to display an error message for certain conditions. The goal is that from table COST, for an object number (that contains TEST + cost center + activity type), gjahr, value type(WRTTP) and version(VERSN), to check if for a certain fixed price per unit measure(TFK001-016) there is data in the table. Thus, if TFKXXX is not maintained in the COST table it will show an error message.
Now what I have done, is using a Call Function, to get the three number period for the TFK field, thus based on the exporting parameter of date/monmit/periv we will get the field of lv_poper which is the period. Then I have done a merge of TFK and lv_poper. Now what I want to do is to check whether a TFK001-016 field is maintained for the key parameters. I cannot do <ls_co_data>-lv_tfkxxx as it does not exist in the table COST. Does anyone have any idea on how can I check if the field TFK001-016 is maintained in the table COST?
CALL FUNCTION 'DATE_TO_PERIOD_CONVERT'
EXPORTING
i_date = lv_date
i_monmit = lv_monmit
i_periv = lv_periv
IMPORTING
e_buper = lv_poper
e_gjahr = lv_gjahr
EXCEPTIONS
input_false = 1
t009_notfound = 2
t009b_notfound = 3
OTHERS = 4.
lv_objnr = 'TEST' + <ls_co_data>-send_cctr + <ls_co_data>-acttype.
lv_tkfxxx = 'TKF' + lv_poper.
LOOP AT lt_cost ASSIGNING FIELD-SYMBOL(<ls_cost>)
WHERE objnr = lv_objnr
AND gjahr = lv_gjahr
AND wrttp = 1
AND versn = 0.
IF lv_tkfxxx IS NOT INITIAL. "The lv_tkfxxx should be checked in the cost table
lv_text = 'Not maintained in ' + lv_objnr + ' for the date ' + <ls_co_data>-postgdate.
ENDIF.
ENDLOOP.
Thank you all in advance!

...
LOOP AT lt_cost ASSIGNING FIELD-SYMBOL(<ls_cost>)
WHERE objnr = lv_objnr
AND gjahr = lv_gjahr
AND wrttp = 1
AND versn = 0.
ASSIGN COMPONENT lv_tkfxxx OF STRUCTURE <ls_cost> TO FIELD-SYMBOL(<v_tkfxxx>).
IF sy-subrc = 0 AND <v_tkfxxx> IS NOT INITIAL.
lv_text = 'Not maintained in ' + lv_objnr + ' for the date ' + <ls_co_data>-postgdate.
ENDIF.
ENDLOOP.

Related

Enhance performance of ABAP report when using OpenSQL query and function call

I have an issue with my ABAP report for it being very slow and taking a long time to complete.
My original report had a query to MARA to get all materials into an iTab, loop over it and then query additional tables for each material and store the necessary data and a function call to READ_TEXT in an output iTab.
Since this process takes very long to compete I changed the code to rely more on the database to handle data access instead of making queries for additionoal data each at a time. But still the time to complete is very long for the amount of data to be fetched.
Current code looks like this
report z_test_report.
types:
begin of _output,
matnr type mara-matnr,
text01 type c length 40,
text02 type c length 40,
ntgew type mara-ntgew,
matkl type mara-matkl,
ean11 type mara-ean11,
preis type p length 6 decimals 2,
bstd type mbew-lbkum,
laeng type marm-laeng,
breit type marm-breit,
hoehe type marm-hoehe,
volum type marm-volum,
end of _output,
t_output type _output.
data:
gt_output type table of t_output,
gs_output like line of gt_output,
gr_table type ref to cl_salv_table,
gr_funct type ref to cl_salv_functions,
gr_columns type ref to cl_salv_columns_table,
gr_column type ref to cl_salv_column.
selection-screen begin of block b01 with frame.
parameters: pa_date type d obligatory.
parameters: pa_kschl type t685-kschl obligatory.
parameters: pa_pltyp type t189-pltyp obligatory.
selection-screen end of block b01.
start-of-selection.
select
m~matnr,
m~ntgew,
m~matkl,
m~ean11,
t~maktx,
b~lbkum,
l~laeng,
l~breit,
l~hoehe,
l~volum
from
mara as m
left join
makt as t
on
m~matnr = t~matnr
and
t~spras = 'D'
left join
mbew as b
on
b~matnr = m~matnr
and
b~bwkey = '1100'
left join
marm as l
on
l~matnr = m~matnr
and
l~meinh = m~meins
into
#data(wa_daten)
order by
m~matnr.
gs_output = corresponding #( wa_daten ).
select single
*
from
a908
where
a908~kappl = 'V'
and
a908~kschl = #pa_kschl
and
a908~vkorg = '1100'
and
a908~pltyp = #pa_pltyp
and
a908~matnr = #wa_daten-matnr
and
a908~datab < #sy-datum
and
a908~datbi > #sy-datum
into
#data(gs_a908).
if ( sy-subrc = 0 ).
select single
*
from
konp
where
konp~knumh = #gs_a908-knumh
into
#data(gs_knop).
if ( sy-subrc = 0 ).
gs_output-preis = gs_knop-kbetr.
endif.
endif.
data:
material_name like stxh-tdname,
textlines like tline occurs 0,
textline like line of textlines.
material_name = gs_output-matnr.
call function 'READ_TEXT'
exporting
id = 'GRUN'
language = 'D'
name = material_name
object = 'MATERIAL'
tables
lines = textlines
exceptions
id = 1
language = 2
name = 3
not_found = 4
object = 5
reference_check = 6
wrong_access_to_archive = 7
others = 8.
if ( sy-subrc = 0 ).
loop at textlines into textline.
concatenate gs_output-text02 textline-tdline into gs_output-text02.
endloop.
endif.
condense gs_output-text02.
append gs_output to gt_output.
endselect.
try.
cl_salv_table=>factory(
exporting
list_display = if_salv_c_bool_sap=>false
importing
r_salv_table = gr_table
changing
t_table = gt_output
).
catch cx_salv_msg.
endtry.
gr_funct = gr_table->get_functions( ).
gr_funct->set_all( abap_true ).
gr_table->display( ).
I think this code can be improved since reading 17k entries from MARA and joining them with the other tables should not be the bottle neck. Changing the old code to the current one reduced the runtime from about 45s down to 30s, removing the READ_TEXT only made the report take about 5s less.
If anyone has an idea it would be really appreciated. Or if anyone knows a tool to measure report performance to find out about bottle necks.

Big Query: how to use Variable and loops with column names

I am trying to use a loop to update a table in Bigquery. My table structure is as following (with 100 columns and thousands of rows):
DATE
PERIOD1
PERIOD2
PERIOD3
PERIOD4
PERIOD5
PERIOD6
PERIOD...
PERIOD100
2021-01-01
row
2021-02-01
row
For each date, I would need to use a loop to populate the values with something like
---
DECLARE VAR_PERIOD INT64 DEFAULT 1
LOOP
IF PERIOD > 100 THEN LEAVE;
END IF;
---
update `mydataset.mytable` set CONCAT('PERIOD',VAR_PERIOD) = (select{+my query})
which obviously cannot work, so I'm wondering what alternative method can be used to easily update my table columns ?
For this you can try using BigQuery API client libraries link.
There are more languages available but I am using python here.
You can directly start in cloud shell. There you can write a Python program to
do your job.
There is some assumption I am taking regarding your requirements :
You want to use SELECT clause to get some value and using which you want to
update values of Period columns.
from google.cloud import bigquery
''' Construct a BigQuery client object. '''
client = bigquery.Client()
query = """
select col_name from `projectID.dataset.table`
where condition
"""
''' Make an API request. '''
query_job = client.query(query)
''' Store the value of query result in some variable (value). '''
for row in query_job:
value = row[0]
'''
Creating the query to update the columns using the value.
UPADTE `projectID.dataset.table`
SET Period1 = Period1 + value, Period2 = Period2 + value ...
where condition
'''
query = "UPDATE `projectID.dataset.table` SET "
for i in range(1,101):
query += 'period'+str(i)+' = ' + 'period'+ str(i) + ' + ' +str(value) +','
query = query[0:-1]
query += ' WHERE condition'
''' Make an API request. '''
query_job = client.query(query)
All BigQuery Update statement must have a WHERE Clause. If you
want to update all the rows than in WHERE condition mention TRUE link.

How do I aggregate negative values with AT END OF and SUM?

I´m working on a ABAP program, in which I have to do a validation for a specific field, like the code below:
SORT t_prcd BY knumh kschl.
LOOP AT t_prcd INTO wa_prcd WHERE knumh = wa_wcocoh-knumh AND kschl = wa_wcocoh-kschl.
IF wa_prcd-vbtyp = 'C'.
wa_prcd-netwr = wa_prcd-netwr * ( -1 ).
wa_prcd-kwmeng = wa_prcd-kwmeng * ( -1 ).
ENDIF.
AT END OF knumh.
SUM.
" SUBTRACT wa_prcd-netwr FROM wa_prcd-netwr.
MOVE EXACT wa_prcd-netwr TO wa_talv-val_vendido.
MOVE wa_prcd-kwmeng TO wa_talv-quant_vendido.
ENDAT.
ENDLOOP.
What I need to do is: if wa_prcd-vbtyp is C the value of wa_prcd-netwr must be negative.
In debugging I see the condition is working fine, but when it comes to SUM, it just does that without sign, which means that instead of sum (-A + -B), it does ( A + B )
Can somebody help me please? Thanks.
When you multiply with -1, you only update the local variable wa_prcd, but not the original table t_prcd. SUM however only takes into account what's in t_prcd.
The fix thus is simply to separate the update from the sum step:
SORT t_prcd BY knumh kschl.
" use a reference or field-symbol to update the table in-place
LOOP AT t_prcd REFERENCE INTO DATA(ref_prcd)
WHERE knumh = wa_wcocoh-knumh AND kschl = wa_wcocoh-kschl
AND vbtyp = 'C'.
ref_prcd->netwr = ref_prcd->netwr * ( -1 ).
ref_prcd->kwmeng = ref_prcd->kwmeng * ( -1 ).
ENDLOOP.
LOOP AT t_prcd INTO DATA(wa_prcd)
WHERE knumh = wa_wcocoh-knumh AND kschl = wa_wcocoh-kschl.
AT END OF knumh.
SUM.
MOVE EXACT wa_prcd-netwr TO wa_talv-val_vendido.
MOVE wa_prcd-kwmeng TO wa_talv-quant_vendido.
ENDAT.
ENDLOOP.

Shortest notation to split ABAP internal table into smaller pieces

In ABAP, I have a pretty large internal table, say 31,000 rows. What's the shortest and most efficient way to split that into multiple smaller tables of fixed size, say 1,000 rows each?
Naive way would be:
DATA lt_next_package TYPE tt_table_type.
LOOP AT it_large_table INTO DATA(ls_row).
INSERT ls_row INTO TABLE lt_next_package.
IF lines( lt_next_package ) >= lc_package_size.
INSERT lt_next_package INTO TABLE rt_result.
CLEAR lt_next_package.
ENDIF.
ENDLOOP.
IF lt_next_package IS NOT INITIAL.
INSERT lt_next_packge INTO TABLE rt_result.
ENDIF.
That works and is rather efficient, but looks cumbersome, esp. the don't-forget-the-last-package section at the very end. I believe there must be a better way to do this with the newer ABAP mesh paths and table expressions, but so far couldn't come up with one.
I am not sure if, there is a right way to do it (sure, there are several ways to do it), but you can try this to overcome the last package problem:
WHILE it_large_table IS NOT INITIAL.
LOOP AT it_large_table ASSIGNING FIELD-SYMBOL(<ls_line>) FROM 1 TO 1000.
INSERT <ls_line> INTO TABLE lt_next_package.
ENDLOOP.
DELETE it_large_table FROM 1 TO 1000.
INSERT lt_next_package INTO TABLE rt_table.
CLEAR: lt_next_package.
ENDWHILE.
Based on JozsefSzikszai's answer, devised another option:
rt_result = VALUE #( FOR i = 1
UNTIL i > round( val = lines( it_large_table) / lc_package_size
dec = 0
mode = cl_abap_math=>round_up )
LET lv_end = i * lc_package_size
lv_start = lv_end - lc_package_size + 1 IN
( VALUE <result-type>(
( LINES OF it_large_table FROM lv_start TO lv_end ) ) ) ).
Somewhat reinvention of both Florian and Jozsef approaches.
Prerequisits:
TYPES:
BEGIN OF line,
rows TYPE string,
slice TYPE bseg_t,
END OF line,
itab TYPE STANDARD TABLE OF line WITH EMPTY KEY,
bseg_t TYPE STANDARD TABLE OF bseg WITH EMPTY KEY.
DATA: result TYPE itab.
Filling large table:
SELECT * UP TO 31000 ROWS
INTO TABLE #DATA(lt_bseg)
FROM bseg.
Here we costruct table of tables which contains slices of the main table by 1000 rows each.
WHILE lt_bseg IS NOT INITIAL.
result = VALUE itab( BASE result
(
rows = | { sy-index * 1000 }-{ sy-index * 1000 + 1000} |
slice = VALUE bseg_t( FOR wa IN lt_bseg INDEX INTO i FROM i + 1 TO i + 1
( LINES OF lt_bseg from i TO i + 999 ) )
)
).
DELETE lt_bseg FROM 1 TO 1000.
ENDWHILE.
Looks somewhat as our requirement, no?
Here are two ways to build a table of subtables AKA pagination:
METHOD prepare_data.
TYPES:
BEGIN OF line,
name TYPE string,
subset TYPE salv_t_row,
END OF line,
itab TYPE STANDARD TABLE OF line WITH EMPTY KEY.
CONSTANTS: lc_package_size TYPE i VALUE 3.
DATA: result TYPE itab,
it_rows TYPE salv_t_row.
DO round( val = lines( it_rows ) / lc_package_size
dec = 0
mode = cl_abap_math=>round_up ) TIMES.
DATA(lv_end) = sy-index * lc_package_size.
DATA(lv_start) = lv_end - lc_package_size + 1.
APPEND INITIAL LINE TO result ASSIGNING FIELD-SYMBOL(<subset>).
<subset>-name = | Subset { sy-index } |.
<subset>-subset = VALUE #( ( LINES OF it_rows FROM lv_start TO lv_end ) ).
ENDDO.
clear result.
result = VALUE itab( FOR i = 1
UNTIL i > round( val = lines( it_rows ) / lc_package_size
dec = 0
mode = cl_abap_math=>round_up )
LET k = 1 IN
(
name = | Subset { i } |
subset = VALUE salv_t_row(
LET
end = i * lc_package_size
start = end - lc_package_size + 1
IN
( LINES OF it_rows from start to end )
)
)
).
Hope this helps. The code here is tested and works. Just copy-paste (and generate some data).

DTP routine to find timestamp range

I am working on a DTP filter routine for a timestamp field in the format of YYYYMMDDhhmmss. I am trying to declare the range as ({timestamp 3 months ago} to {current timestamp}). I am very new to ABAP and basically have the code set up right now so it doesn't have any syntax errors. I am currently having trouble getting the correct timestamp to be filled when I assign it to "l_ts".
*$*$ begin of routine - insert your code only below this line *-*
BREAK-POINT.
DATA: l_idx LIKE sy-tabix,
l_ts TYPE rstimestmp,
l_ts2 TYPE rstimestmp.
READ TABLE l_t_range WITH KEY
fieldname = 'TIMESTAMP'.
l_idx = sy-tabix.
* Capture the Current Date
l_ts = sy-datlo + sy-timlo.
l_ts2 = ( sy-datlo + sy-timlo ) - 93.
IF l_idx <> 0.
* fill the Selection table.
l_t_range-low = l_ts.
l_t_range-sign = 'I'.
l_t_range-option = 'BT'.
l_t_range-high = l_ts2.
MODIFY l_t_range INDEX l_idx.
ELSE.
* fill the Selection table.
l_t_range-fieldname = 'TIMESTAMP'.
l_t_range-low = l_ts.
l_t_range-high = l_ts2.
l_t_range-sign = 'I'.
l_t_range-option = 'BT'.
APPEND l_t_range.
ENDIF.
p_subrc = 0.
*$*$ end of routine - insert your code only before this line *-*
You're mixing up timestamps and discrete date and time calculations - won't work this way. What you'll really want is probably something like this:
DATA: l_date TYPE d,
l_time TYPE t,
l_ts TYPE rstimestmp.
FIELD-SYMBOLS: <ls_param> LIKE LINE OF l_t_range.
* ensure that the parameter exists
READ TABLE l_t_range ASSIGNING <ls_param> WITH KEY fieldname = 'TIMESTAMP'.
IF sy-subrc <> 0.
APPEND INITIAL LINE TO l_t_range ASSIGNING <ls_param>.
<ls_param>-fieldname = 'TIMESTAMP'.
ENDIF.
<ls_param>-sign = 'I'.
<ls_param>-option = 'BT'.
* "from" date = three months ago, more or less - probably the start of the day?
l_date = sy-datlo - 93.
l_time = '000000'. " or sy-timlo.
CONVERT DATE l_date TIME l_time INTO TIME STAMP l_ts TIME ZONE sy-zonlo.
<ls_param>-low = l_ts.
* "to" date = today - probably the end of the day?
l_date = sy-datlo.
l_time = '235959'. " or sy-timlo.
CONVERT DATE l_date TIME l_time INTO TIME STAMP l_ts TIME ZONE sy-zonlo.
<ls_param>-high = l_ts.