Dynamically read an internal table with more than one key field - abap

Let us say I have the following code:
iob_typedescr ?= cl_abap_typedescr=>describe_by_name( 'HOUSES' ).
iob_structdescr_table ?= iob_typedescr.
it_ddic_all = iob_structdescr_table->get_ddic_field_list( ).
LOOP AT it_ddic_all INTO is_ddic WHERE keyflag EQ 'X'.
APPEND is_ddic TO it_keyfields.
ENDLOOP.
So basically, in the above code I know all of the key fields in the table HOUSES and they are in the table it_keyfields. Now if I had an internal table which had the same structure as HOUSES and wanted to select data from it how would I be able to do this dynamically? If I knew I had only one key then I could do a READ TABLE as follows:
READ TABLE it_internal_table WITH KEY (key_name) = provided_value TRANSPORTING NO FIELDS.
But in this case I may have more than one key in it_keyfields so I am not sure how I could write the READ TABLE statement for this.

You could create a dynamic where condition and use it with LOOP WHERE.
Here is a snippet using table SPFLI as an example.
" variable that contains dynamic where condition later
DATA lv_where TYPE string.
" field-symbol that can contain any data
FIELD-SYMBOLS: <f_field_value> TYPE any.
" define it_internal_table (can be dynamic)
DATA it_internal_table TYPE TABLE OF spfli.
it_internal_table = VALUE #(
( mandt = '001' carrid = '002' connid = '0003' cityfrom = 'PARIS' )
( mandt = '001' carrid = '004' connid = '0005' cityfrom = 'BERLIN' )
).
" define provided values that need to be searched in it_internal_table
DATA(ls_provided_entry) = VALUE spfli( mandt = '001' carrid = '002' connid = '0003' cityfrom = 'PARIS' ).
" create dynamic where condition
LOOP AT it_keyfields ASSIGNING FIELD-SYMBOL(<f_key>).
ASSIGN COMPONENT <f_key>-fieldname OF STRUCTURE ls_provided_entry TO <f_field_value>.
IF sy-subrc = 0.
IF lv_where IS INITIAL.
lv_where = |{ <f_key>-fieldname } = `{ <f_field_value> }`|.
ELSE.
DATA(lv_where_and) = |AND { <f_key>-fieldname } = `{ <f_field_value> }`|.
CONCATENATE lv_where lv_where_and INTO lv_where SEPARATED BY space.
ENDIF.
ENDIF.
ENDLOOP.
" now check with LOOP WHERE if the provided entry exists in it_internal_table
LOOP AT it_internal_table ASSIGNING FIELD-SYMBOL(<f_internal_table_line>) WHERE (lv_where).
" if this point is reached, the entry exists;
" <f_internal_table_line> contains the complete line
WRITE 'The entry exists'.
EXIT.
ENDLOOP.

If you want to handle different numbers of key fields dynamically at runtime then you won't be able to do that with a READ command. However, you can now also read data from an internal table using SELECT and there you can dynamically create a WHERE condition that selects on fields only known during runtime.
From the documentation: Use of SELECT to access an internal table using as an alternative to the statement READ TABLE. Unlike READ TABLE, the statement SELECT offers a (dynamic) WHERE condition and evaluates the field list for the inline declaration. The statement is executed on the AS ABAP and the data in the internal table is not transported to the database.

Related

CDS View parameters in-line syntax form or not?

During a select from CDS with parameters, the into table does it always have to be in-line declaration??
MRP:
#AbapCatalog.sqlViewName: 'ZCDS_PARAM1'
#AbapCatalog.compiler.compareFilter: true
#AccessControl.authorizationCheck: #CHECK
#EndUserText.label: 'Test parameters'
define view ZCDS_PARAM
with parameters
part_number : matnr // Input parameter
as select from mara
{
key matnr as material,
mtart as material_type,
matkl as material_group
}
where
matnr = :part_number
ABAP Program:
SELECT *
FROM zcds_param( part_number = '000000001000001234' )
INTO TABLE #DATA(lt_material). -->should it always be in-line here??
IF sy-subrc IS INITIAL .
WRITE : 'Material Exists!'.
ENDIF.
Inline declarations are not mandatory, but the new syntax is
So you need the # sign, and commas between field names.
(source: I tried it in a 7.53 system)
No, not necessarily.
You can also declare the variable explicitly as TYPE TABLE OF zcds_param (or alternatively a local structure type which has all the fields you want with the correct name and type) and then use INTO CORRESPONDING FIELDS OF TABLE:
DATA lt_material TYPE TABLE OF zcds_param.
SELECT *
FROM zcds_param( part_number = '000000001000001234' )
INTO CORRESPONDING FIELDS OF TABLE #lt_material.

The field "str_mara" is unknown, but there is a field with the similar name "it_mara"

I want to display the content of Mara table.
types : begin of str_mara,
matnr type mara-matnr,
ernam type mara-ernam,
end of str_mara.
data it_mara type table of str_mara .
select matnr ernam from mara into TABLE it_mara .
loop at it_mara into str_mara.
write:/ str_mara-matnr , str_mara-ernam.
endloop.
Well, there is no variable named str_mara. There is just a type named str_mara.
Just loop using a field symbol as it should be done anyway.
LOOP AT it_mara ASSIGNING FIELD-SYMBOL(<str_mara>).
WRITE: /, <str_mara>-matnr, <str_mara>-ernam.
ENDLOOP.
Type is just a static definition, no memory is allocated, therefore cannot be used on its own.
You can either create a variable with that type or use a inline declaration to create a variable like that.
option 1: data ls_mara type str_mara.
option 2: loop at lt_mara into data(ls_mara).
Or go with Umar's answer :)
BTW, be sure to check your where condition on the access to mara table.
You can also use inline declaration to display content of mara table with fewer line of ABAP code.
SELECT matnr, ernam FROM mara INTO TABLE #DATA(lt_mara) .
LOOP AT lt_mara ASSIGNING FIELD-SYMBOL(<fs_mara>).
WRITE: /, <fs_mara>-matnr, <fs_mara>-ernam.
ENDLOOP.

How to dynamically call Field Symbols

Lets say I have a standard structure assigned to a field symbol. Is there a way to pass a variable to the field symbol for dynamic calls?
Eg:
DATA: lt_mara TYPE STANDARD TABLE OF mara WITH HEADER LINE,
parameter_name(10) TYPE c.
parameter_name = 'MATNR'.
LOOP AT lt_mara ASSIGNING FIELD-SYMBOL(<fs_lt_mara>).
WRITE: <fs_lt_mara>-(parameter_name).
ENDLOOP.
Where the parameter_name contains a column name that is available in the mara structure.
It is possible with ASSIGN instruction.
Below is an example:
DATA: lt_mara TYPE STANDARD TABLE OF mara WITH HEADER LINE,
parameter_name(10) TYPE c.
parameter_name = 'MATNR'.
FIELD-SYMBOLS: <fs_lt_mara> TYPE mara,
<fs_value> TYPE any.
SELECT * FROM mara UP TO 10 ROWS INTO TABLE lt_mara.
LOOP AT lt_mara ASSIGNING <fs_lt_mara>.
ASSIGN COMPONENT parameter_name OF STRUCTURE <fs_lt_mara> TO <fs_value>.
IF sy-subrc = 0.
WRITE: / <fs_value>.
ENDIF.
ENDLOOP.
I provide another example of ASSIGN. You do ASSIGN once and do a LOOP INTO.
DATA: lt_mara TYPE STANDARD TABLE OF mara,
ls_mara TYPE mara,
lc_matnr TYPE char5 VALUE 'MATNR'.
FIELD-SYMBOLS: <ls_mara> TYPE mara,
<lv_value> TYPE any.
SELECT * FROM mara UP TO 10 ROWS INTO TABLE lt_mara.
ASSIGN COMPONENT lc_matnr OF STRUCTURE ls_mara TO <lv_value>.
CHECK <lv_value> IS ASSIGNED.
LOOP AT lt_mara INTO ls_mara.
WRITE: / <lv_value>.
ENDLOOP.

What is wrong with the following ABAP code

REPORT zbc400_figure157.
TYPES: BEGIN OF t_conn,
cityfrom TYPE spfli-cityfrom,
cityto TYPE spfli-cityto,
carrid TYPE spfli-carrid,
connid TYPE spfli-connid,
END OF t_conn.
DATA:
conn_list LIKE STANDARD TABLE OF t_conn,
startline LIKE sy-tabix,
BEGIN OF wa_travel,
dest TYPE spfli-cityto,
cofl_list LIKE conn_list,
END OF wa_travel,
travel_list LIKE SORTED TABLE OF wa_travel WITH UNIQUE KEY dest.
FIELD-SYMBOLS:
<fs_conn> TYPE t_conn,
<fs_conn_int> TYPE t_conn,
<fs_travel> TYPE wa_travel.
PARAMETERS pa_start TYPE spfli-cityfrom DEFAULT 'FRANKFURT'.
SELECT carrid cityfrom cityto
FROM spfli
INTO CORRESPONDING FIELDS OF TABLE conn_list.
SORT conn_list BY cityfrom cityto ASCENDING AS TEXT.
** build up nested table.
LOOP AT conn_list ASSIGNING <fs_conn> WHERE cityfrom = pa_start.
CLEAR wa_travel.
wa_travel-dest = <fs_conn>-cityto.
READ TABLE conn_list
WITH KEY cityfrom = wa_travel-dest
TRANSPORTING NO FIELDS
BINARY SEARCH.
startline = sy-tabix.
LOOP AT conn_list ASSIGNING <fs_conn_int>
FROM startline.
IF <fs_conn_int>-cityfrom <> wa_travel-dest.
EXIT.
ENDIF.
APPEND <fs_conn_int> TO wa_travel-cofl_list.
ENDLOOP.
SORT wa_travel-cofl_list BY cityto carrid ASCENDING AS TEXT.
INSERT wa_travel INTO TABLE travel_list.
ENDLOOP.
Error: Field "T_CONN" is unknown. It is neither in one of the specified tables nor defined by a "DATA" statement . . . . . . . . . .
We declare an inner table COFL_LIST and an outer table TRAVEL_LIST with corrosponding work areas. An internal table CONN_LIST buffers all of the flight connections and sorts them.
The program uses 3 tables, inner table, outer table and an internal table.
I made the changes in the program as suggested by LPK:
conn_list TYPE STANDARD TABLE OF t_conn,
However, now the problem is in line:
FIELD-SYMBOLS:
<fs_conn> TYPE t_conn,
<fs_conn_int> TYPE t_conn,
<fs_travel> TYPE wa_travel.
Error: The type "WA_TRAVEL" is unknown.
In the program wa_travel variable is already defined in it's BEGIN OF wa_travel and END OF wa_travel block. Why can't the system pick this up?
This line is wrong:
conn_list LIKE STANDARD TABLE OF t_conn,
T_CONN is a type and therefore you have to use TYPE instead of LIKE.
You can find an explanation about the difference here.
The declaration is not correct...
FIELD-SYMBOLS:
TYPE t_conn,
TYPE t_conn,
TYPE wa_travel.
Should be
FIELD-SYMBOLS:
TYPE t_conn,
TYPE t_conn,
LIKE wa_travel.
You need to check with the link provided in the previous answer... or you can use the following from the Help Portal on TYPE & LIKE

search dynamically from any table

I want to make a function module that make the same query, for example:
Select column_id from table_name where column_name = name_value.
I want to pass the table_name, column name and name_value, so, no matter what table is, I can get the id of the provided name.
Could you lead me in how to do that in abap using function modules?
Let's say you took the following parameters as input.
DATA: table_name TYPE string VALUE 'MARA',
column_id TYPE string VALUE 'MATNR',
column_name TYPE string VALUE 'MTART',
name_value TYPE string VALUE 'HALB'.
First, dynamically create a table of the type you will select into.
DATA: results TYPE REF TO data,
tablety TYPE string.
FIELD-SYMBOLS <results> TYPE STANDARD TABLE.
tablety = table_name && '-' && column_id.
CREATE DATA results TYPE TABLE OF (tablety).
ASSIGN results->* TO <results>.
Then use a dynamic query to fill the table.
DATA: condition TYPE string.
condition = column_name && ` = name_value`.
SELECT (column_id) FROM (table_name)
INTO TABLE results
WHERE (condition).
Pass back the generically-typed reference to the calling program.