I need to count the number of duplicate rows in an internal table base on one field.
I have tried to create a work area and counting the duplicate data but the problem is it counts all duplicate data. My purpose is to count duplicate data by the same date.
DATA: gv_line TYPE i.
gv_line = 0.
LOOP AT i_sect_proe.
IF wa_sect_proe IS INITIAL.
wa_sect_proe = i_sect_proe.
CONTINUE.
ENDIF.
IF wa_sect_proe-/smr/wondat EQ i_final_f-/smr/wondat.
gv_line = gv_line + 1.
ENDIF.
i_sect_proe-/smr/line = gv_line.
ENDLOOP.
The code I've tried displays the number off all duplicate data.
DATA: BEGIN OF lt_result OCCURS 0, date TYPE datum, count TYPE i, END OF lt_result.
SORT yourTable BY dateField.
LOOP AT yourTable.
lt_result-date = yourTable-dateField.
lt_result-count = 1.
COLLECT lt_result INTO lt_result.
ENDLOOP.
Result in lt_result[].
Related
In the below code, I read data from database table with SELECT into the internal table dresult.
Reading the first line of the internal table displays the expected result, but using the LOOP displays nothing.
What's the issue?
Note: I tried to remove ENDSELECT and it worked. Why?
REPORT ZTEST02.
"Detail Informaion
TYPES : BEGIN OF details,
d1 type arktx, " description
d2 type lfimg, "quantity
END OF details.
DATA : dresult TYPE TABLE OF details WITH HEADER LINE,
t_dresult TYPE details,
sdno TYPE vbeln.
PARAMETERS packo TYPE vbeln OBLIGATORY MATCHCODE OBJECT f4_likp.
START-OF-SELECTION.
SELECT arktx,lfimg
INTO #dresult
FROM lips as detail
LEFT JOIN marm as material
ON detail~matnr = material~matnr
LEFT OUTER JOIN vbak
ON detail~vgbel = vbak~vbeln
WHERE detail~vbeln = #packo.
ENDSELECT.
READ TABLE dresult into t_dresult INDEX 1.
write: t_dresult-d1,t_dresult-d2.
LOOP AT dresult INTO t_dresult.
write: t_dresult-d1,t_dresult-d2.
ENDLOOP.
With SELECT ... ENDSELECT you select the data into a work area (INTO #dresult - which is actually the header line of the internal table with the same name). As result the internal table dresult does not contain any data, so the LOOP and the READ TABLE won't work.
I would declare the internal table without HEADER LINE and remove ENDSELECT:
DATA: dresult type STANDARD TABLE OF details,
...
SELECT ...
INTO TABLE #dresult
Moreover, after READ TABLE, it is always good to check if an entry was found:
READ TABLE ...
IF sy-subrc EQ 0.
...
ENDIF.
Hi ABAP users I would like to ask if what process I can make to COLLECT the data on the same field? all I want to do is to sum up or collect the data in dmbtr that belongs to same date, (date field monat) (werks to plant codes)
it_zfi_vbrp_bseg_1-num3 = it_zfi_vbrp_bseg_1-werks.
it_zfi_vbrp_bseg_1-num2 = it_zfi_vbrp_bseg_1-dmbtr.
COLLECT it_zfi_vbrp_bseg_1.
DELETE ADJACENT DUPLICATES FROM it_zfi_vbrp_bseg_1 COMPARING ALL FIELDS.
Sort it_zfi_vbrp_bseg_1 by werks.
LOOP AT it_zfi_vbrp_bseg_1 into wa_zfi_vbrp_bseg_1 WHERE monat = '01'.
IF wa_zfi_vbrp_bseg_1-werks EQ '4030'.
WRITE:/, AT pos wa_zfi_vbrp_bseg_1-dmbtr.
ENDIF.
ENDLOOP.
Any configuration in my codes guys?
Suppose, you already extended standard bseg table with monat field, then you should do like this:
TYPES: BEGIN OF ty_zfi_vbrp_bseg_1,
werks TYPE bseg-werks,
monat TYPE monat,
dmbtr TYPE bseg-dmbtr,
END OF ty_zfi_vbrp_bseg_1.
DATA: it_zfi_vbrp_bseg_1 TYPE TABLE OF ty_zfi_vbrp_bseg_1,
is_zfi_vbrp_bseg_1 TYPE ty_zfi_vbrp_bseg_1.
SELECT werks, monat, dmbtr
INTO TABLE #DATA(lt_bseg)
FROM bseg
WHERE werks = '4030'.
* summation of months
LOOP AT lt_bseg ASSIGNING FIELD-SYMBOL(<fs_line>).
CLEAR: is_zfi_vbrp_bseg_1.
MOVE-CORRESPONDING <fs_line> TO is_zfi_vbrp_bseg_1.
COLLECT is_zfi_vbrp_bseg_1 INTO it_zfi_vbrp_bseg_1.
ENDLOOP.
* output the results
LOOP AT it_zfi_vbrp_bseg_1 ASSIGNING FIELD-SYMBOL(<zfi_line>).
IF <zfi_line>-werks EQ '4030'.
WRITE: / <zfi_line>-werks, <zfi_line>-monat, <zfi_line>-dmbtr.
ENDIF.
ENDLOOP.
Couple of notes to your incorrect code:
COLLECT doesn't work like single statement and should be executed in loop.
To sum with COLLECT statement you should declare work area so that all non-key fields would be numeric. Key fields (even if it is implicit key) can be of any type, as opposed to what Ray said. N type is also OK.
DELETE ADJACENT DUPLICATES is redundant here, because after COLLECT (which makes summation by primary key) you won't have any dups.
This all comes down to how you define your internal table it_zfi_vbrp_bseg_1
For COLLECT to work, you need to define it with character type keys followed by packed field types:
data: begin of it_zfi_vbrp_bseg_1 occurs 0,
werks type werks,
month(2) type c,
dmbtr type dmbtr,
end of it_zfi_vbrp_bseg_1.
That will work.
This will not:
data: begin of it_zfi_vbrp_bseg_1 occurs 0.
include structure vbrp.
data: monat type monat,
dmbtr type dmbtr,
end of it_zfi_vbrp_bseg.
Read the help on COLLECT and define your summary table accordingly.
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 ) ) ).
Is there any simplest/fastest way for below code:
DATA: lv_knuma_ag LIKE STANDARD TABLE OF zta_fg_hdr WITH HEADER LINE.
RANGES: lr_knuma_ag FOR zta_fg_hdr-knuma_ag.
"Select `knuma_ag` and save it to internal table `lv_knuma_ag`
LOOP AT li_promo.
SELECT dest~knuma_ag
FROM zta_fg_dest AS dest
INNER JOIN zta_s_d_xkunnr AS xkunnr ON xkunnr~zcd_dest EQ dest~zcd_dest
APPENDING CORRESPONDING FIELDS OF TABLE lv_knuma_ag
WHERE dest~knuma_ag EQ li_promo-knuma_ag
AND xkunnr~xkunnr EQ gi_vl-vkbur.
ENDLOOP.
"Insert `lv_knuma_ag` onto ranges table `lr_knuma_ag`
LOOP AT lv_knuma_ag.
lr_knuma_ag-sign = 'I'.
lr_knuma_ag-option = 'EQ'.
lr_knuma_ag-low = lv_knuma_ag-knuma_ag.
lr_knuma_ag-high = space.
APPEND lr_knuma_ag.
ENDLOOP.
"Delete row in `li_promo` where `knuma_ag` is in ranges table `lr_knuma_ag`
IF NOT lr_knuma_ag IS INITIAL.
DELETE li_promo WHERE knuma_ag IN lr_knuma_ag.
ENDIF.
As you see on the code, it contains twice loop to only fill the ranges table. I would like to make it only one, is it possible? Thanks.
Yes, it should be possible to achieve this without flooding your DBMS with possibly thousands of queries:
DATA: lt_knuma_ag_hash TYPE HASHED TABLE OF insert_type_of_knuma_ag_here WITH UNIQUE KEY TABLE_table_line.
IF li_promo[] IS NOT INITIAL.
SELECT DISTINCT knuma_ag
FROM zta_fg_dest
INTO TABLE lt_knuma_ag_hash
FOR ALL ENTRIES IN li_promo
WHERE knuma_ag = li_promo-knuma_ag.
ENDIF.
LOOP AT li_promo ASSIGNING <li_promo>.
READ TABLE lt_knuma_ag_hash TRANSPORTING NO FIELDS WITH TABLE KEY table_line = <li_promo>-knuma_ag.
IF sy-subrc = 0.
DELETE li_promo.
ENDIF.
ENDLOOP.
How do I get the row count of an internal table? I guess that I can loop on it. But there must be a saner way.
I don't know if it makes a difference but the code should run on 4.6c version.
There is also a built-in function for this task:
variable = lines( itab_name ).
Just like the "pure" ABAP syntax described by IronGoofy, the function "lines( )" writes the number of lines of table itab_name into the variable.
You can use the following function:
DESCRIBE TABLE <itab-Name> LINES <variable>
After the call, variable contains the number of rows of the internal table .
Beside the recommended
DESCRIBE TABLE <itab-Name> LINES <variable>
there is also the system variable SY-TFILL.
From documentation:
After the statements DESCRIBE TABLE, LOOP AT and READ TABLE, the number of rows of the accessed internal table.
Example script:
REPORT ytest.
DATA pf_exclude TYPE TABLE OF sy-ucomm WITH HEADER LINE.
START-OF-SELECTION.
APPEND '1' TO pf_exclude.
APPEND '2' TO pf_exclude.
APPEND '3' TO pf_exclude.
APPEND '4' TO pf_exclude.
WRITE: / 'sy-tfill = ', sy-tfill.
DESCRIBE TABLE pf_exclude.
WRITE: / 'sy-tfill = ', sy-tfill, 'after describe table'.
sy-tfill = 0. "Reset
READ TABLE pf_exclude INDEX 1 TRANSPORTING NO FIELDS.
WRITE: / 'sy-tfill = ', sy-tfill, 'after read table'.
sy-tfill = 0. "Reset
LOOP AT pf_exclude.
WRITE: / 'sy-tfill = ', sy-tfill, 'in loop with', pf_exclude.
sy-tfill = 0. "Reset
ENDLOOP.
The result:
sy-tfill = 0
sy-tfill = 4 after describe tabl
sy-tfill = 4 after read table
sy-tfill = 4 in loop with 1
sy-tfill = 0 in loop with 2
sy-tfill = 0 in loop with 3
sy-tfill = 0 in loop with 4
Please get attention of the value 0 for the 2nd entry: SY-TFILL is not updated with each step, only after the first loop.
I recommend the usage SY-TFILL only, if you need it direct after the READ(1)... If there are other commands between the READ and the usage of SY-TFILL, there is always the danger of a change of the system variable.
(1) or describe table.
DATA : V_LINES TYPE I. "declare variable
DESCRIBE TABLE <ITAB> LINES V_LINES. "get no of rows
WRITE:/ V_LINES. "display no of rows
Refreance:
http://www.sapnuts.com/courses/core-abap/internal-table-work-area.html
The functional module EM_GET_NUMBER_OF_ENTRIES will also provide the row count. It takes 1 parameter - the table name.
you can also use OPEN Sql to find the number of rows using the COUNT Grouping clause and also there is system field SY-LINCT to count the lines(ROWS) of your table.
if I understand your question correctly, you want to know the row number during a conditional loop over an internal table.
You can use the system variable sy-tabix if you work with internal tables. Please refer to the ABAP documentation if you need more information (especially the chapter on internal table processing).
Example:
LOOP AT itab INTO workarea
WHERE tablefield = value.
WRITE: 'This is row number ', sy-tabix.
ENDLOOP.
data: vcnt(4).
clear vcnt.
LOOP at itab WHERE value = '1'.
add 1 to vcnt.
ENDLOOP.
The answer will be 3. (vcnt = 3).
I don't think there is a SAP parameter for that kind of result. Though the code below will deliver.
LOOP AT intTab.
AT END OF value.
result = sy-tabix.
write result.
ENDAT.
ENDLOOP.