Anthony Cecchini is the President of Information Technology Partners (ITP), an SAP consulting company headquartered in Pennsylvania. ITP offers comprehensive planning, resource allocation, implementation, upgrade, and training assistance to companies. Anthony has over 20 years of experience in SAP R/3 business process analysis and SAP systems integration. His areas of expertise include SAP NetWeaver integration; ALE development; RFC, BAPI, IDoc, Dialog, and Web Dynpro development; and customized Workflow development. You can reach him at [email protected]
ABAP Dynamic Programming Techniques
OK, lets pick up from last month where we attempted to accurately define what Dynamic Programming. We said … Dynamic programming is the use of special programming constructs that enable you to use selected features of data and/or operations at runtime that cannot, for various reasons, be determined at compile time.
Lets keep this definition in mind as we begin this discussion on DYNAMIC Programming Techniques.
Using a Dynamic Table Names
Let begin by creating a very simple program that will display table names to the user and when clicked on, the user is presented with the number of rows in the table. Look at the code below
REPORT ztony. DATA: tabname TYPE tabname, count TYPE i. START-OF-SELECTION. WRITE: / 'EBAN', / 'MARA', / 'VBAK'. AT LINE-SELECTION. READ CURRENT LINE LINE VALUE INTO tabname. SELECT COUNT(*) FROM (tabname) INTO count. WRITE: 'The table', tabname(7), 'contains', count, 'entries.'.
We have manufactured the “Requirement” that the user will NOT KNOW THE TABLE NAME at compile time, but will at runtime. This is exactly what our definition above states will require us to make use of Dynamic Program Techniques.
The program, at the event line-selection, will take the content of the selected line and read it into the field tabname. In the SELECT statement, the name of the database table is not specified statically. Instead, the clause from (tabname), denotes that the name of the database table is to be read from the variable tabname and substituted into the SELECT statement. So when the SELECT statement is executed, the field tabname is parsed for a valid name of a database table. If tabname doesn’t contain a valid name, an exception occurs.
OK, lets keep evolving this little program… let’s say instead of the total count of records, we actually want the rows returned. OK, now we are talking about SELECTING rows into a work-area. But wait! We don’t know which table the user will choose, so how can we TYPE the appropriate work-area? We can, trust me. First we need to learn a little about Generic Types and Field-Symbols. But, I’ll even go up a level of abstraction and ask, what the heck is a TYPE?
ABAP is a hybrid programming language with a type system that reflects both the runtime-event-oriented and the object oriented programming models. Object types (e.g.,classes and interfaces) and value types (e.g., structures and tables) are integrated into one type system and share the same namespace.
Think of types as blueprints for a house. Blueprints provide descriptions of houses. Types are just descriptions of data. This analogy provides an easy way to distinguish between types and data.
A type is defined as “a set of properties that apply to all data of this type,” while data is a sequence of bytes in the memory of the program. A type that contains the complete description required to create data is called a concrete type. In contrast, a generic type is only a partial description of data — for example, the length is not defined within the type. Since additional information is needed to fully describe the data, you cannot create data from a generic type.
Well by using Generic Data Types like ANY, or DATA we can create DATA REFERENCES. We can construct an appropriate work area via the command CREATE DATA. It allows you to create data objects with a dynamically specified type at runtime.
But before we start coding, we need to understand another ABAP construct, the Field-Symbol (cue the music..Da..Da…Da..Daaaaa)
Field symbols are a special feature of ABAP not found in most other programming languages. Their primary purpose is for accessing lines of internal tables, without the need of actually copying them. Think of field symbols like an aliases for existing data. You can assign an alias or new name to existing data (or part of it), and perform work on that data under its new name. Field symbols can be typed with generic types. Before using a field symbol, you must first declare it and give it a type. Its name must begin with “<” and end with “>”.
OK, lets look at the code below…
REPORT ztony. DATA: dref TYPE REF TO data, tabname TYPE tabname. FIELD-SYMBOLS: <row> TYPE any, <component> TYPE any. START-OF-SELECTION. WRITE: / 'EBAN', / 'MARA', / 'VBAK'. AT LINE-SELECTION. READ CURRENT LINE LINE VALUE INTO tabname. * dynamically create appropriate Data Structure CREATE DATA dref TYPE (tabname). ASSIGN dref->* TO <row>. * fetch the data SELECT * FROM (tabname) UP TO 2 ROWS INTO <row>. * display the result NEW-LINE. DO. ASSIGN COMPONENT sy-index OF STRUCTURE <row> TO <component>. IF sy-subrc <> 0. EXIT. " no more components ENDIF. WRITE: <component>. ENDDO. ENDSELECT.
So lets break this code Down. First, a variable dref with type REF TO DATA is declared, which can hold a reference to any data object. At this point we have not TYPED the Data yet. We just create the reference. I can hear you asking what a reference actually means, so… a reference is a data description that holds a reference to data. It simply points to the data, like a data signpost. We also declare our field symbols.
DATA: dref TYPE REF TO data.
FIELD-SYMBOLS: <row> TYPE any, <component> TYPE any.
Next, a work area with line type tabname is created dynamically by the ABAP command CREATE DATA. Here we have TYPED the data to the contents of the field tabname, which is filled by the user when they click on the table they want. So if they clicked on EBAN, the Data Reference dref is typed to the structure EBAN.
CREATE DATA dref TYPE (tabname).
The reference dref is referring to this work area. It cannot be accessed directly. Instead, a field symbol <row> is assigned to the work area referred by dref. This now allows us to safely fetch the data from the table tabname into <row> because it is guaranteed that the database table is type-compatible with the work area. I limit the selection to only 2 rows, but you get the idea. Please remember I am inside a SELECT-ENDSELECT loop so I am only selecting one row at a time.
ASSIGN dref->* TO <row>.
SELECT * FROM (tabname) UP TO 2 ROWS INTO <row>.
Now, that I have FETCHED a row and can access it via the Field-Symbol <row>, we can use a DO Loop to Loop over all components of <row>, and display the fetched data contents.
DO. ASSIGN COMPONENT sy-index OF STRUCTURE <row> TO <component>. IF sy-subrc <> 0. EXIT. " no more components ENDIF. WRITE: <component>. ENDDO. ENDSELECT.
OK, I can hear you saying, Tony, in other blogs you have written you have cautioned us from using the SELECT_ENDSELECT construct as it does not perform well, and besides, this output is messy! You are correct! An array select is the preferred way of selecting rows into an internal table. So …..
Dynamically Created Internal Tables
It is possible to create not only a structures with a dynamically specified type, but also internal tables with a dynamically specified line type. With this feature, we can rewrite our last example. Here we use an array fetch instead of a SELECT -ENDSELECT loop. Look at the code below.
REPORT ztony. DATA: tabname TYPE tabname, dref TYPE REF TO data, alv TYPE REF TO cl_gui_alv_grid. FIELD-SYMBOLS: <itab> TYPE ANY TABLE. START-OF-SELECTION. WRITE: / 'EBAN', / 'MARA', / 'VBAK'. AT LINE-SELECTION. READ CURRENT LINE LINE VALUE INTO tabname. * dynamically create appropriate internal table CREATE DATA dref TYPE TABLE OF (tabname). ASSIGN dref->* TO <itab>. * fetch the data SELECT * FROM (tabname) up to 100 rows INTO TABLE <itab>. * display the result CREATE OBJECT alv EXPORTING i_parent = cl_gui_container=>screen0. CALL METHOD alv->set_table_for_first_display EXPORTING i_structure_name = tabname CHANGING it_outtab = <itab>.
The array fetch will reduce the execution time significantly because multiple round-trips to the database interface will be avoided.
Instead of displaying our result set as a list, we show it via an ALV grid control. To do so, we first create an instance of the class cl_gui_alv_grid and assign the entire screen as its parent. Then, the set_table_for_first_display method is called, submitting the dynamically specified table name tabname and the result set <itab> to the grid control.
Stay tuned, as we continue to look at Dynamic Programming, we will learn how to code Dynamic Where Clauses for our select statements, how to code Dynamic joins, Dynamic updates, and Class based Exception handling. Don’t forget to take a look at ABAP Dynamic Programming – Part 1 for an introduction to the concept of Dynamic Programming.
Take a look at these videos for some good Dynamic Programming Tutorial examples on field symbols and how they are used with internal tables.