Skip to Content

The most important news for internal tables are the Table expressions that allow the same single line access as the READ TABLE statement. But soon after those were available, the question arised “What about the system fields?”.

We all use the well known patterns


IF sy-subrc = 0.




DATA(idx) = sy-tabix.

Since an expression should be free of side effects it especially should not influence any system field. So what about these patterns?

The answer comes in form of two new built-in functions.

Line existence

A new predicate function that returns true if a line exists:

IF line_exists( itab[ … ] ).


Where itab[ … ] is a table expression that works here like READ TABLE … TRANSPORTING NO FIELDS.

(Note that we have already some other predicate functions in ABAP: matches and the contains-variants in the context of string processing).

Line index

A new table function that returns the number of the line in the index used (primary or secondary index):

DATA(idx)= line_index( itab[ … ] ).

Where itab[ … ] is a table expression that works here like READ TABLE … TRANSPORTING NO FIELDS. If no line is found, the value 0 is returned. For Hash-Tables or hashed secondary keys, always the value -1 is returned.

(Note that this is the second table function in ABAP. The other one is lines).

To report this post you need to login first.


You must be Logged on to comment or reply to a post.

  1. Uwe Fetzer

    A short question to the predicate functions, and I don’t know whether you are able / allowed to answer or not:

    The result of a predicate function is always a truth value, but

    A data type for Boolean data objects to take truth values is currently not supported.

    (see )


    If we want to create our own predicate functions we need such a type. Is it planned in the (near) future?

    1. Horst Keller Post author

      This old question …


      A Boolean data type is in discussion, planned since I am in  the ABAP group – and this is quite a time. To be honest, I can’t fullly explain why it isn’t done yet. One reason can be that any introduction of a new built-in type is dangeroius for compatibility. If we introduce a type named “boole” or “boolean” you will have naming collisions with self defined types in ABAP programs or in the Dictionary of that name. We made that experience recently with int8. int8 could be introduced in the NGAP release (8.x) because this ABAP release is not downward compatible. But up to now int8 did not find its way into 7.40.


      For the time being you still have two work with type c length 1 aka abap_bool with values abap_true and abap_false as workaround. The Boolean functions boolc and boolx also support this access.


      But since you will participate in our TechEd session as a mentor this year, you will very probalbly have the chance to discuss that with us directly

      1. Uwe Fetzer

        Yes, indeed, this is an old question, and I’ll repeat this question for every new release. Promised

        Sure, we can handle it with a workaround, but in my opinion the kernel could do the same (is the returning parameter initial -> false else true). We don’t really need a new datatype.


        See you in Amsterdam (finally )

        1. Former Member

          the kernel could do the same (is the returning parameter initial -> false else true)



          This won’t work in all cases – take for example ISH_TRUE_FALSE which is a CHAR value with ‘0’ signifying false and ‘1’ signifying true. The initial value would be ‘ ‘…

          1. Horst Keller Post author

            Yep, only a real Boolean would really help. Problem is that too many and different workarounds can be found.


            B.T.W.,  price question, what is the result of


            IF boolc( 1 = 2 ) = abap_false.


            True or false and why?


            1. Uwe Fetzer

              I don’t think we need a workaround here. Every expression (let it be a variable or a returning parameter) which is not initial is true like in most of the other languages.


              boolc( 1 = 2 ) = abap_false

              is false because boolc is a string and abap_false is a char with length 1.

  2. Former Member

    Hi Horst,


    For moving data from one Internal Table to another, of same type,

    we are having a handy option,


    itab_1[] = itab_2[].


    But the we do not have any similar syntax, to move data from

    one Internal Table to another, of different type, as we have

    for work areas ,


    move-corresponding ls_tab_1 to ls_tab_2.


    Just wondering,

    why can not we have such function even for Internal Tables,

    or else if we do already have some similar syntax,

    please post.


    Thanking You All.

  3. Jacques Nomssi

    Hello Horst,


    which idiom would recommend for the ubiquitous SY-SUBRC check after a READ TABLE ?


           rv_msg = mt_return[ 1 ]message.

           CATCH cx_sy_itab_line_not_found.

             CLEAR rv_msg.



    IF line_exists( mt_return[ 1 ] ).

       rv_msg = mt_return[ 1 ]message.


      CLEAR rv_msg.



    or anything else?





    1. Horst Keller Post author

      Before 7.40, SP08 you only have the above two and I would recommend line_exists.


      With 7.40, SP08 there will be


      VALUE #( mt_return[ 1 ]message DEFAULT def ) and

      VALUE #( mt_return[ 1 ]message OPTIONAL )


      For non-existing lines you can define your own default value and check the result for that one.


      rv_msg = VALUE #( mt_return[ 1 ]message DEFAULT def ).

      IF rv_msg = def.




      Or in your case above, you simply denote the initial value a follows:


      rv_msg = VALUE #( mt_return[ 1 ]message OPTIONAL ).





  4. Jacques Nomssi

    Hello Horst,


    what is the differene between a table function and a functional operand? e.g. error message Arithmetic calculation not permitted here is triggered for


         MESSAGE s542(>4) WITH lines( mt_data[] ).

    I can use


    DATA(lv_count) = lines( mt_data[] ).

         MESSAGE s542(>4) WITH lv_count

    but it is legal to write

    MESSAGE s035(mv) WITH lo_stream->get_filename( ).

    so why can’t I use a table function in an expression at a functional operand position? It seems to me it works in some contexts, but I could not find a reference yet.



    1. Horst Keller Post author

      Hi Jacques,


      Unfortunately, this is a little bit tricky because ABAP is growing very evolutionary and especially for operand positions it is not always clear what you can place there.


      But fortunately, you have (my) documentation which (at least) tries to describe it right for all these possibilities.


      In your case, you can look up  MESSAGE …WITH :

      If the data type is character-like, then dobj1 through dobj4 are character-like expression positions; if it is not character-like, they are functional operand positions.


      Well, in your first example “lines( mt_data[] ).” as a built-in function is neither character-like nor an operand for a functional operand position.


      For built-in functions you need a general expression position.


      But you can also make it easily character-like:


      … WITH |{ lines( mt_data[] ) }|.

  5. Amadeus Grabmayer

    Hi Horst,


    this might be a dumb question, but why is it not supported to return the value of “line_exists”?


    I get an error if I try the following:

    rv_assigned = line_exists( lt_roles[ table_line = iv_role ] ).


    The following works (obviously), but an IF is actually what I wanted to avoid due to the table function.

    IF line_exists( lt_roles[ table_line = iv_role ] ).

      rv_assigned = abap_true.



    (I am currently on a System with 7.40 SP7)




    1. Horst Keller Post author

      Hello Amadeus,


      This touches the old question (discussed many times) of Boolean types in ABAP.


      ABAP does not support a real boolean type and I guess it will never do.


      Therefore, you cannot assign a relational expression (and that’s what the predicate function LINE_EXISTS is) to a variable but you can use it only where logical expressions are evaluated (behind IF and …).


      As a workaround, you can use

      flag = line_index( itab[ … ] ).

      A value 0 in flag means not assigned.





      1. Amadeus Grabmayer

        Hello Horst,


        thank you for your quick reply!


        So the one-liner (that I get a ABAP_BOOL value) would be the following?

        rv_bool = boolc( line_index( itab[…] ) GT 0 ).




  6. Guyon Cumby

    Would it make sense to permit functions such as:


    IF ( line_exists( lt_serial_tc[ sernr <> 0 ] ) ).


    or is there a more obvious way than this?

    1. Horst Keller Post author

      Would it make sense

      Yes, but unfortunately not supported since table expressions work like READ TABLE and READ TABLE does not support arbitrary logical expressions.

      or is there a more obvious way than this?

      No …

  7. Guru Anandh Ramakrishnan



    Thanks for the informative blog. I  have a question..


    Is it possible to have dynamic string clause within line_index statement?

    For example something like


    FIELD-SYMBOLS:   <fs_t_data>   TYPE table.

    // Some logic to determine lv_string_key which is the row key

    line_index<fs_t_data>[ lv_string_key ] ).



    How do we get the index/tabix for the ‘for loop’ in a statement below?

    Currently sy-index/sy-tabix does not return the correct row of wa.

    rt_selected_lines VALUE (BASE rt_selected_lines FOR wa IN <fs_t_data> WHERE (lv_string_keytabixsyindex )  ).


    Thanks & Best Regards,




      1. Guru Anandh Ramakrishnan

        Hi Horst,

        Thanks a lot for the quick feedback.


        The second problem is now solved with INDEX INTO . Thanks a lot for the reference..  🙂


        However, I still get the error in first one. To elaborate, my code looks something like this

        FIELD-SYMBOLS:   <fs_t_data>   TYPE table.  // This is a dynamic table whose structure is known only at runtime

        // A dynamic runtime logic results in lv string_key values as below that uniquely identifies one row of <fs_t_data>

        lv_string_key = ‘field1 = value1 field2 = value2’.


        line_index<fs_t_data>[ lv_string_key ] ).  // I get an error here..

        line_index( <fs_t_data>[ KEY lv_string_key ] ).  // I tried with prefix KEY as well.. I still get an error


        We are on NW750 SP 005. 

        Could you please help?


        Thanks & Regards,



        1. Horst Keller Post author

          Seems that you haven’t read the documentation I sent you.

          Is there a dynamic clause for table expressions? No. Is there a dynamic clause for READ TABLE? No. And now the sixty–four–dollar question: Where do we have a dynamic clause for reading internal tables?

          1. Guru Anandh Ramakrishnan

            Sixty-four-dollar answer.. 🙂

            I know READ TABLE does not support dynamic clause.

            However,  I was trying to understand whether the newer syntax supports a dynamic clause so that we can avoid a LOOP statement that supports dynamic where clause.

            Thanks a lot for your response Horst..

            Best Regards,


  8. Sunil Sankar

    Hi All,

    Do we have CONTINUE like statement which can be used in FOR loop to do nothing in a COND -> ELSE block?

    SPAN {
    font-family: “Courier New”;
    font-size: 10pt;
    color: #000000;
    background: #FFFFFF;
    .L0S32 {
    color: #3399FF;
    .L0S52 {
    color: #0000FF;
    .L0S55 {
    color: #800080;
    .L0S70 {
    color: #808080;
    lt_sect VALUE lty_sectBASE lt_sect  FOR ws IN results INDEX INTO l_cntr
    LET  l_nrec VALUE #results[ l_cntr + ]line OPTIONAL )
    l_trow VALUE #p_gt_raw_data[ l_nrec ]row OPTIONAL )
    l_crow p_gt_raw_data[ wsline ]row
    l_prow wsline – 39
    l_sect p_gt_raw_data[ l_prow ]value+0(10)  IN
    VALUE #sec   l_sect
    frm   l_crow + 1
    to    l_trow – )
    ELSE Continue???  )



      1. Horst Keller Post author

        Sorry, no, we havn’t such a thing. For your use case we could also use a construct to insert no line into an internal table. But we don’t have that either.

  9. Amy King

    I notice we can write

    IF line_exists( itab[ ... ] ).

    but not

    CASE abap_true.
      WHEN line_exists( itab[ ... ] ).

    Even though the predicate returns a Boolean. Is there a reason the latter isn’t supported?

    The syntax for a method specification is “objref->method” or
    “class=>method”. “objref->method” or “class=>method”. “class=>method”.


    1. Horst Keller Post author



      The operands operand1, operand2, … after WHEN are extended functional operand positions in which, however, table expressions cannot be specified. This property of operand positions is obsolete and should no longer be used.

      Don’t ask. Normally, you should use constants behind WHEN while the expressions are behind CASE. But for whatsoever reasons, it was implemented wrongly, years ago, in the pre-real-expression era.





Leave a Reply