Mike Pokraka
Posted onJune 23, 2018 2 minute read
ABAP tips: Checking existence within an internal table
FollowRSS feedLike
42 Likes 34,072 Views 30 Comments
I got a blog-worthy surprise when I did a quick performance test on two different ways to use the new-ish (7.4) table expressions to perform a search on an internal table.
My scenario involved internal table searches where the values would be missing a lot of the time. The unexpected result was that when a row can’t be found, using table expressions with exceptions is ten times slower on average than all other lookup methods, including the good ol’ fashioned READ TABLE with SY-SUBRC.
Quick primer for those still getting accustomed to 7.4 ABAP (feel free to skip to the results):
With table expressions, finding an entry in a table is really neat. I have a table that includes a field “ID”. So the following READ TABLE can be rewritten using a table expression (the bit in square brackets):
READ TABLE itab INTO row WITH KEY id = find_id.row = itab[ id = find_id ].TRY.row = itab[ id = find_id ].CATCH cx_sy_itab_line_not_found....
ENDTRY.Another alternative is to use a VALUE constructor expression where you can add the keyword OPTIONAL (or DEFAULT) to just initialize any non-found values.
row = VALUE #( itab[ id = find_id ] OPTIONAL ).IF row IS INITIAL....
This particular usage of VALUE may seem a little awkward. I mean, why use x = VALUE #( y ) when you can just write x = y? The VALUE constructor’s sole purpose here is the OPTIONAL bit that lets us do away with the exception.
As I was working on a performance-sensitive component, I tested it to see what performs best.
For completeness, and additionally with suggestions by Jacques Nomssi, Quynh Doan Manh and Uwe Fetzer in the comments, I also added a couple of other ways to look for an entry in a table:
The trusty READ TABLE … WITH KEY, with and without TRANSPORTING NO FIELDS
line_exists( )
ASSIGN itab[ … ] – which (bizarrely) sets SY-SUBRC instead of producing an exception
REF #( ) instead of VALUE #( ).
I tested a million failed lookups on a 7.50 HANA system. Here are the results in microseconds:
line_exists( ) : 610,758
READ TABLE ... TRANSPORTING NO FIELDS : 671,368READ TABLE : 671,115ASSIGN itab[ ... ] : 707,929
REF #( itab[ ... ] OPTIONAL ) : 888,562
VALUE #( itabl[ ... ] OPTIONAL ) : 961,803
TRY ... itab[ ... ] CATCH : 6,325,265I did not expect the last one at all.
So the take-away here for me is that a TRY-CATCH may be easier to read, but should not be used in performance-sensitive code unless you expect the values to be found most of the time. I’m happy to sacrifice a little performance for readability, but this is a significant impact.
I suspect that this applies to CATCH blocks in general, but that’s another analysis for another day.
For comparison, I also re-ran the same but this time with a lookup value that did exist. Two things happened:
the exception was no longer an influence (to be expected)
line_exists( ) became a poor choice in my scenario, because we need to double up the the search in order to also read the record:
IF line_exists( itab[ id = find_id ] ).row = itab[ id = find_id ].If you don’t need the data, line_exists( ) is fastest.
If performance is number 1 priority and you need the data, READ TABLE is fastest.
For compact and/or readable code, use table expressions.
(Yes, I know, ‘new’ ABAP can be used to make code either more readable or more cryptic, but that’s another discussion)
TRY-CATCH with table expressions can be a useful way to structure your code (e.g. to use a single catch handler for multiple expressions), but be mindful of the expected failure rate and performance-criticality of the component. If we’re talking anything less than thousands in a short space of time then you can safely ignore the impact.After several comments and suggestions, I uploaded my test program to GitHub at abap_itab_perf
It was just a quick-n-dirty originally for comparing just two options and just grew as a copy/paste of alternatives. Feel free to play with it or even send any updates back via GitHub.