Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
matt
Active Contributor

Introduction

Given the reluctance of the general ABAP community to use new-fangled (that is, over fifteen years old) concepts like SORTED and HASHED tables, I was hesitant to write about something a bit newer, but then I thought - what the heck, perhaps some people will find it an encouragement to use new stuff!

And I know this isn't that new!

So, we have HASHED tables, where the key is unique and the lookup time is constant for each record, and SORTED tables which mean we don't need BINARY SEARCH any more (except if we need sort descending...). For these tables, there's an index already defined to speed things up - but it's like a database table with just a primary index. Secondary keys are like additional indexes on database tables - but for internal tables.

If you don't know how SORTED and HASHED tables then read the documentation. It's all quite comprehensive so no excuse!

I've heard it said that you should only use these if you've got tables with loads of information in. Well, so long as the data isn't being handled in a loop, I think it doesn't matter. If the data volume being processed is small, a few extra nano-seconds won't matter, and data volumes grow - so there's some future proofing in using the structures which are most efficient with large tables, right from the start.

Secondary keys

Here's that syntax, to refresh your memory.

TYPES dtype { {TYPE tabkind OF [REF TO] type}

            | {LIKE tabkind OF dobj} }

            [tabkeys]

            [INITIAL SIZE n].

And then tabkeys looks like this:

... [ WITH key ]
    [ WITH secondary_key1 ] [ WITH secondary_key2 ] ...
    [ {WITH|WITHOUT} FURTHER SECONDARY KEYS ] ... .

Additions

1. ... WITH FURTHER SECONDARY KEYS

2. ... WITHOUT FURTHER SECONDARY KEYS

Those additions, we'll forget about. They're for use when you're defining generic table types.

Now, for my purposes, I've got a questionnaire, with pages on it, categories of questions and questions. And I need to access it in many ways. So here's how I defined it:

TYPES:

     questionnaire_ty TYPE SORTED TABLE OF q_entry_ty WITH NON-UNIQUE KEY page_number cat_seq

                      WITH NON-UNIQUE SORTED KEY by_question COMPONENTS q_id

                      WITH NON-UNIQUE SORTED KEY by_cat_guid COMPONENTS cat_guid q_seq

                      WITH NON-UNIQUE SORTED KEY by_cat_text COMPONENTS cat_text

                      WITH NON-UNIQUE SORTED KEY by_cat_seq  COMPONENTS cat_seq .

The idea is that I can access an internal table of this type rapidly by page number, question id, category unique id (guid), category text and category sequence. Seems quite a lot, but the alternatives were to have a standard table and sort it and use binary search for each read, or not bother at all, and just put up with sequential reads.

Some problems

I've got the categories in my questionnaire in sequence order. So, naturally, I want to renumber them. The obvious way of doing this is

LOOP AT me->questionnaire ASSIGNING <entry> USING KEY by_cat_guid WHERE cat_guid EQ i_guid.

   ADD 1 TO index.

   <entry>-cat_seq = index.

ENDLOOP.

But there's a problem there. It dumps. And it dumps because cat_seq is part of the key by_cat_guid!

So, I thought, I'll delete the records, collect them and then insert them afterwards

LOOP AT me->questionnaire INTO entry USING KEY by_cat_guid WHERE cat_guid EQ i_guid.

   DELETE TABLE me->questionnaire FROM entry.

   entry-cat_seq = index.

    INSERT entry INTO TABLE renumbered.

ENDLOOP.

INSERT LINES OF renumbered INTO TABLE me->questionnaire

But data was still going amiss. The problem was, that the delete command deletes the entry that matches the primary key. So it was reading one entry in the LOOP AT, and deleting an entirely different entry (that matched the primary key) at the DELETE.

I tried the old DELETE... INDEX, but that got me nowhere. But a quick check of the syntax for DELETE gave me the hint.

LOOP AT me->questionnaire INTO entry USING KEY by_cat_guid WHERE cat_guid EQ i_guid.

   DELETE TABLE me->questionnaire FROM entry USING KEY by_cat_guid.

   entry-cat_seq = index.

   INSERT entry INTO TABLE renumbered.

ENDLOOP.

INSERT LINES OF renumbered USING KEY by_cat_guid INTO TABLE me->questionnaire

What to be aware of

With an internal table with additional keys, there are few things you really need to take care about.

1. You can't change a field of an entry you've ASSIGNED to, if that field is part of one of the keys

2. If you access data using one key - you really need to change it using the same key.

3. All of the usual internal table handling statements have the addition USING KEY. Sometimes it's vital - like with the DELETE example. Other times it's a matter of performance. For the INSERT LINES I could have omitted the USING KEY, and it would still work - however it is not as efficient, since I know that all my renumbered entries have the same cat_guid.

Final words

When new ABAP commands become available, try to use them. In my application, it probably won't make any difference. But what you don't use, you forget. Surely there will come a time when you do need additional accesses to internal tables - if you've already practiced, the next time it won't take as long.

25 Comments