Introduction and motivation

Sometimes you’re writing a report and you need to access a configuration table several times (like company codes) or you need to access some master data across your program.

In this blog I’d like to give you a structure, how you can safely enable local caching of table entries within your programs, to improve performance and readability, as the caching algorithms guarantee, that a SELECT statement for a particular table (entry) is done only once for the whole duration of the program.

The usage of static variables or global variables should be avoided, wherever possible, as it leads to a bad programming style. However we use the STATICS statement here, that encapsulates a program-wide variable into a single form routine. That kind of usage isn’t allowed in the ABAP OO context, anymore. If you’d like to have the caching algorithm within the ABAP OO context, your static variable will become class instance, and the selecting form routine a static method. That way, you could even share the cache across different programs within one execution unit.

But let’s start more simple. And this is with ABAP/4 form routines.

How to use it

The caching mechanism will only work, if you access your table through a single method only. As a matter of fact, you’ll breach this deal, when youstart duplicate SELECT statements again, not reading from the cache.

By encapsulating the table accesses, you’ll also be able to track down the access points of the table(s) more simply, by using the where-used-list of the form routine. Your program implementation will become more readable and more easy to maintain.

The example usage will always have such a format, like the one for example table T001, that holds the company codes. Keep in mind, that there’re already some caching or access routines in place by the SAP Standard, so you may also switch the actual select statement into another function call.

Idiom for full table cache

Create a form routine (when using classical ABAP/4) for read access to your table.

Within the form routine:

FORM select_single_<table to cache>

   STATICS: lt_cache TYPE STANDARD TABLE

                       OF <table to cache>.

   IF lt_cache IS INITIAL.

      “Fill cache

   ENDIF.

   READ TABLE lt_cache INTO <return structure>

                       WITH KEY key(s) = <given key field(s)>

ENDFORM.

Idiom for single line table cache

FORM select_single_<table to cache>

   STATICS:      lt_cache      TYPE STANDARD TABLE

                                  OF <table to cache>.

   READ TABLE lt_cache INTO <return structure>

                       WITH KEY key(s) = <given key field(s)>

   IF sy-subrc <> 0.   “missed

      “Fill cache line

      SELECT SINGLE * FROM <table to cache>

                      INTO <return structure> WHERE

      <when found, insert entry into cache>

   ENDIF.

ENDFORM.


Full table cache


REPORT ZADV_TABLE_CACHING_FULL.

PARAMETERS: pv_bukrs             TYPE BUKRS.      “t001-bukrs

START-OF-SELECTION.

PERFORM start_of_selection.

FORM start_of_selection.

   “Lookup on a full table cache
   DATA: ls_t001                  TYPE T001.

   PERFORM select_t001 USING    pv_bukrs
                       CHANGING ls_t001.
   IF sysubrc <> 0.
      WRITE:/ ‘Entry not found for BUKRS=’, pv_bukrs.
   ELSE.
      WRITE:/ ‘Entry T001=’, ls_t001bukrs, ls_t001butxt“,…
   ENDIF.

ENDFORM.

Here you can see, that the call for the table lookup should be made very simple, at best, you put it all into one single line of coding. In my examples I have put it into several lines to make it easier for you to read.

Now let’s see, how the caching will work, and we start with the algorithm that just reads the complete content of table into the cache. You can use this technique when you expect a limited low number of table entries.


**********************************************************************
*             Local static cache, full table
**********************************************************************

FORM select_t001   USING    iv_bukrs  TYPE BUKRS
                   CHANGING cs_t001   TYPE T001.

   STATICS: lt_t001                 TYPE STANDARD TABLE
                                         OF T001
                                         INITIAL SIZE 50.

   “Full cache
   IF lt_t001[] IS INITIAL.
      “Pre-fill the cache now
      SELECT * FROM t001 INTO TABLE lt_t001 ORDER BY PRIMARY KEY. “#EC CI_NOWHERE
   ENDIF.

   READ TABLE lt_t001 INTO cs_t001
                      WITH KEY bukrs = iv_bukrs
                      BINARY SEARCH.

   “At the end of the FORM-call, the sy-subrc <> 0 will indicate
   “the not-found state, otherwise the changing parameter has valid data

ENDFORM.

First of all you’ll notice the definition of the cache, in this case, a STANDARD TABLE. A sorted table might not work in any situation when reading the full table, but feel free to change it as you need it.

The second section will pre-fill the cache, whenever it has not been initialized yet. The statement is very fast, so it is okay to execute it for every table access on the cache later on. The trade-off to make a pre-fill is better than an unsuccessful READ TABLE before, and then filling the cache, and then reading from the cache again.

The cache lookup is performed by a simple READ TABLE, and we add a search direction, which you can omit, if you like.

The result is now either

   sy-subrc = 0  and the table entry of the changing parameter structure has valid data

or it is

   sy-subrc <> 0  and there’s no valid data.

You may also CLEAR the result data, when the read access wasn’t successful, but I’d advice to stick to one strategy only.

Caching of single table entries

Caching single table lines will be much the same, but now we put the cache lookup upfront, and maintain the cache afterwards on any missing entry. This way the cache will grow for every different selection on demand.

**********************************************************************
*             Local static cache, single table entries
**********************************************************************
FORM select_lfa1        USING    iv_lifnr  TYPE LIFNR
                        CHANGING cs_lfa1   TYPE LFA1.

   STATICS: lt_lfa1                 TYPE SORTED TABLE
                                         OF LFA1
                                         WITH UNIQUE KEY LIFNR   “Adapt accordingly
                                         INITIAL SIZE 50.

   “First cache lookup
   READ TABLE lt_lfa1 INTO cs_lfa1
                      WITH TABLE KEY lifnr = iv_lifnr.
   IF sysubrc <> 0.
      “Not found in cache
      SELECT SINGLE * FROM LFA1
                      INTO cs_lfa1
                      WHERE lifnr = iv_lifnr.
      IF sysubrc = 0.
         “The entry exists and is now already put into the changing/returning structure
         “Then add it to the cache now
         INSERT cs_lfa1 INTO TABLE lt_lfa1.
      ENDIF.
   ENDIF.

   “At the end of the FORM-call, the sy-subrc <> 0 will indicate
   “the not-found state, otherwise the changing parameter has valid data

ENDFORM.


So, here we start with the cache lookup. You can adapt the read table with a BINARY SEARCH extension, when you’re using a standard table (which is sorted).

If the cache was missed, we maintain it by re-using the existing changing variable (that fills the returning element with the same statement).

The generic versions

If you have to access a lot of different cached tables in your program, you might think of using a form generator in the form of macro definition.

**********************************************************************
*    Generic table cache on any table with one primary key field
*    in addition to MANDT field (if present)
*
*    Caches full table
*
**********************************************************************

DEFINE cached_table_full.   “&1 = table name
                            “&2 = fieldname of key
                            “&3 = data element of key field

*** Generic local static cache, single table entries
    FORM select_&1          USING    iv_&2     TYPE &3
                            CHANGING cs_&1     TYPE &1.

       STATICS: lt_&1                   TYPE STANDARD TABLE
                                             OF &1
                                             INITIAL SIZE 50.

       “Full cache
       IF lt_&1[] IS INITIAL.
          “Pre-fill the cache now
          SELECT * FROM &1 INTO TABLE lt_&1 ORDER BY PRIMARY KEY. “#EC CI_NOWHERE
       ENDIF.

       READ TABLE lt_&1   INTO cs_&1
                          WITH KEY &2 = iv_&2
                          BINARY SEARCH.

       “At the end of the FORM-call, the sy-subrc <> 0 will indicate
       “the not-found state, otherwise the changing parameter has valid data

    ENDFORM.

END-OF-DEFINITION.

and you define your form routine like this:

cached_table_full   TCURC WAERS WAERS_CURC.    “Define form for full   cache on TCURC table

And the single-entry version is like this:

**********************************************************************
*    Generic table cache on any table with one primary key field
*    in addition to MANDT field (if present).
*
*    Caches single entries
*
**********************************************************************

DEFINE cached_table_single. “&1 = table name
                            “&2 = fieldname of key
                            “&3 = data element of key field

*** Generic local static cache, single table entries
    FORM select_&1          USING    iv_&2     TYPE &3
                            CHANGING cs_&1     TYPE &1.

       STATICS: lt_&1                  TYPE SORTED TABLE
                                             OF &1
                                             WITH UNIQUE KEY &2   “Adapt accordingly
                                             INITIAL SIZE 50.

       “First cache lookup
       READ TABLE lt_&1   INTO cs_&1
                          WITH TABLE KEY &2 = iv_&2.
       IF sysubrc <> 0.
          “Not found in cache
          SELECT SINGLE * FROM  &1
                          INTO  cs_&1
                         WHERE &2 = iv_&2.
          IF sysubrc = 0.
             “The entry exists and is now already put into the changing/returning structure
             “Then add it to the cache now
             INSERT cs_&1 INTO TABLE lt_&1.
          ENDIF.
       ENDIF.

       “At the end of the FORM-call, the sy-subrc <> 0 will indicate
       “the not-found state, otherwise the changing parameter has valid data

    ENDFORM.

END-OF-DEFINITION.

and you define your form routine like this:


cached_table_single T024  EKGRP EKGRP.         “Define form for single cache on T024  table

There might be an ABAP OO version coming up.

So stay tuned.

   Florin

Please ->rate  or  ->like

if you find this blog post useful or if you’d like to give feedback.

changelog: added simplified idiom description in HOW TO USE IT section

To report this post you need to login first.

Be the first to leave a comment

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

Leave a Reply