
This blog has been inspired by bruno.esperanca and his thought provoking The last runtime buffer you'll ever need? The thing is, I wrote such a thing a few years ago, that's widely used by one of my clients. There's a few areas it could be improved
interface zif_lookup
public .
constants c_dateto type fieldname value 'DATETO'. "#EC NOTEXT
methods lookup
exporting
es_data type any
eo_type type ref to cl_abap_structdescr
e_notfound_flag type char1
exceptions
sql_error .
methods set_key_val
importing
i_component type clike
i_value type any .
methods set_tim_val
importing
i_component type clike
i_value type any .
methods set_val_component
importing
i_component type clike .
methods get_ref2_lookup
returning
value(rp_data) type ref to data
exceptions
sql_error .
methods get_val_struc
returning
value(ro_valstruc) type ref to cl_abap_structdescr .
methods get_notfound_flag
returning
value(r_notfound_flag) type flag .
endinterface.
Well, the constructor is missing, so just showing you the interface doesn't really help! So, imagine a class with this attribute.
DATA: buffer TYPE REF TO zif_lookup.
Then we have a method in some class that reads materials from the MARC table, with i_matnr, i_werks, exporting e_bwtty and e_mmsta.
IF buffer IS NOT bound.
CREATE OBJECT buffer TYPE zcl_table_lookup EXPORTING
i_table_name = 'MARC'
i_whole_tab = abap_false.
" The data I want to get back
buffer->set_val_component( 'BWTTY' ).
buffer->set_val_component( 'MMSTA' ).
ENDIF.
" The key data
buffer->set_key_val( i_component = 'MATNR' i_value = i_matnr )
buffer->set_key_val( i_component = 'WERKS' i_value = i_werks ).
" Now look it up.
DATA: BEGIN OF looked_up,
bwtty TYPE bwtty_d,
mmsta TYPE mmsta,
END OF looked up.
buffer->lookup( IMPORTING es_data = looked_up ).
e_bwtty = looked_up-bwtty.
e_mmsta = looed_up-mmsta.
What the class ZCL_TABLE_LOOKUP does is take the supplied components through set_val_component and set_key_val, and constructs two hashed tables (using RTTS) with the key components (in this case, MATNR and WERKS) as key. The first contains data looked up, the second contains data looked up and not found.
After the first lookup, you can't change the component fields.
If the i_whole_tab parameter is set, then the whole table will be buffered, rather than doing line by line buffering.
I created a BW style look up, that uses the table look up above, and the same interface. It specialises into two further classes - one for looking up InfoObject master data, the other reading from a DSO.
Their constructors take an InfoObject name / DSO name, convert that to the underlying transparent tables, and instantiates a table lookup instance for this table. Oh - and for InfoObjects there's a flag on the constructor for whether the data is time dependent.
The component setting methods similarly take InfoObject names (instead of field names). The InfoObject names are then converted to field names, and passed to the table lookup instance.
I'll concentrate on the TABLE buffer implementation, as it is that which is the engine.
There are two HASHED internal table, both with the same structure of fieldname, value and fieldtype (keyed on fieldname). The field type is simply gained via DD03L, using the table and fieldname.
The val_component (the fields which define which values will be returned) doesn't populate the value field. Values for the key fields are held as strings.
This method is used for time dependent data.
No buffer tables are created until the first time this method runs. Using RTTS, I build a few table types and references to these a few tables based on those types. The types of these variables, I'll leave as a exercise for the readers - they're not difficult to work out
* Add key fields
LOOP AT me->th_key_fields ASSIGNING <ls_field>.
ls_component-name = <ls_field>-fieldname.
ls_component-type ?= cl_abap_tabledescr=>describe_by_name( <ls_field>-fieldtype ).
APPEND ls_component TO lt_tab_component.
APPEND ls_component TO lt_ntim_component.
APPEND <ls_field>-fieldname TO lt_key.
ENDLOOP.
* Time dependent fields
IF me->s_tim_field IS NOT INITIAL.
ls_component-name = me->s_tim_field-fieldname.
ls_component-type ?= cl_abap_tabledescr=>describe_by_name( me->s_tim_field-fieldtype ).
APPEND ls_component TO lt_tab_component.
APPEND me->s_tim_field-fieldname TO lt_key.
ls_component-name = c_index_fld.
ls_component-type ?= cl_abap_tabledescr=>describe_by_data( sy-tabix ).
APPEND ls_component TO lt_ntim_component.
ENDIF.
* Add value fields for buffer
LOOP AT me->th_val_fields ASSIGNING <ls_field>.
ls_component-name = <ls_field>-fieldname.
ls_component-type ?= cl_abap_tabledescr=>describe_by_name( <ls_field>-fieldtype ).
READ TABLE lt_tab_component TRANSPORTING NO FIELDS WITH KEY name = ls_component-name.
IF sy-subrc IS NOT INITIAL.
APPEND ls_component TO lt_tab_component.
ENDIF.
APPEND ls_component TO lt_val_component.
ENDLOOP.
* Create table structure and types
lo_tablestruc = cl_abap_structdescr=>create( lt_tab_component ).
lo_hash_table_type =
cl_abap_tabledescr=>create( p_line_type = lo_tablestruc
p_table_kind = cl_abap_tabledescr=>tablekind_hashed
p_unique = abap_true
p_key = lt_key ).
lo_sort_table_type =
cl_abap_tabledescr=>create( p_line_type = lo_tablestruc
p_table_kind = cl_abap_tabledescr=>tablekind_sorted
p_unique = abap_true
p_key = lt_key ).
* Create "time key" structure and table
IF me->s_tim_field IS NOT INITIAL.
DELETE lt_key WHERE name EQ me->s_tim_field-fieldname.
lo_ntim_struc = cl_abap_structdescr=>create( lt_ntim_component ).
lo_ntim_table_type =
cl_abap_tabledescr=>create( p_line_type = lo_ntim_struc
p_table_kind = cl_abap_tabledescr=>tablekind_hashed
p_unique = abap_true
p_key = lt_key ).
CREATE DATA me->ps_ntim_lookup TYPE HANDLE lo_ntim_struc.
CREATE DATA me->pth_ntim_lookup TYPE HANDLE lo_ntim_table_type.
ENDIF.
* Create value structure types
me->o_valstruc = cl_abap_structdescr=>create( lt_val_component ).
* Create pointers to the lookup table, workarea and values
CREATE DATA me->pth_lookup TYPE HANDLE lo_hash_table_type. " The main buffer
CREATE DATA me->ps_lookup TYPE HANDLE lo_tablestruc. " The key fields in a structure
CREATE DATA me->ps_val TYPE HANDLE me->o_valstruc. " The values being read, in a structure
CREATE DATA me->pth_lookup_nf TYPE HANDLE lo_hash_table_type. " The buffered "not found" values
Then we need to read the data. For a whole table buffer, it's a simple piece of dynamic SQL. Note how pool tables are handled. It doesn't actually matter if we get duplicates selected; it's just less efficient.
TRY.
SELECT DISTINCT (me->t_select) FROM (me->table) INTO TABLE <lt_lookup>.
CATCH cx_sy_dynamic_osql_error cx_sy_open_sql_db INTO lx_error.
" Handle the fact that you can't use DISTINCT with pool tables
TRY.
SELECT (me->t_select) FROM (me->table) INTO TABLE <lt_lookup>.
CATCH cx_sy_dynamic_osql_error cx_sy_open_sql_db INTO lx_error.
RAISE sql_error. "<---- this really should be a proper class based exception. I'm sorry...
ENDTRY.
ENDTRY.
Now, we can actually get data. <ls_lookup> is constructed from ps_lookup, and contains the key values.
* See if the value is already in the table
READ TABLE <lth_lookup> INTO <ls_lookup> FROM <ls_lookup>.
If it's there, we're done. <ls_lookup> with contain the key fields and value fields. Just pass the value fields back to the calling program. If it isn't there we have to check the <lth_lookup_nf> table (ok, for whole table buffering we don't need to; we know it's not found if it isn't in <lth_lookup>. With line by line buffering, we have to do a single dynamic SQL read. Here, the time dependent reads may come into play.
lt_where = me->get_where_tab( ).
TRY.
SELECT (me->t_select) FROM (me->table) INTO xs_lookup WHERE (lt_where) ORDER BY (me->orderby). " Orderby is only set for time dependence
IF not time dependent OR date GT date in the key.
EXIT.
ENDIF.
ENDSELECT.
CATCH cx_dy_dynamic_osql_error cx_sy_open_sql_db.
raise SQL_ERROR.
ENDTRY.
If the data isn't found, then it's added to the not found table, otherwise it's added to the found table.
Simple, isn't it?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
2 | |
2 | |
2 | |
2 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 |