Google Search - Blog...........

ABAP - Comparing Two Internal Tables - A Generic Approach.

• Change Documents
The program shows a generic approach for comparing the contents of two internal tables. The internal tables which represent "PBO" data and "PAI" data must have the same line type. As a result the program returns the entries that are new, modified or deleted.
The program takes advantage of the SAP standard logic for preparing change documents yet clearly simplifies the comparison task for developers.
The function has been realized as (local) class with two static methods:
• COMPARE (functional method)
• TEST (test method)
However, in order to fully exploit this functionality it should be realized as global class in your SAP system.
Method Signatures
The static method TEST generates simplified PBO data and PAI data for demonstrating the functionality of method COMPARE. PBO data and PAI data as well as the results are displayed as ALV lists. The interface of method TEST contains three IMPORTING parameters (flags) for simulating PAI data that contain new, modified or deleted entries and any combination thereof.
After displaying the PBO and PAI data method TEST calls method COMPARE whose results are displayed as ALV lists, too. If there is no difference between the PBO and PAI data method COMPARE returns an empty list for new entries (INSERT). The same is true for modified (UPDATE) and deleted entries (DELETE).
Simulated PBO and PAI data (identical records)
In order to simulate different sitations for data comparison mark one or more of the checkboxes on the selection screen. The TEST method displays the PAI and PBO data of the scenario followed by the changed data (INSERT, UPDATE, DELETE).
Simulated PBO and PAI data (different records)
*&---------------------------------------------------------------------*
*& Report Z_SDN_ITAB_COMPARISON
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*

REPORT z_sdn_itab_comparison.

INCLUDE z_sdn_itab_comparison_c01.


PARAMETERS:
p_ins AS CHECKBOX DEFAULT ' ',
p_upd AS CHECKBOX DEFAULT ' ',
p_del AS CHECKBOX DEFAULT ' '.

" NOTE: Mark the checkboxes in order to simulate a scenario.

START-OF-SELECTION.


*The static method TEST generates simplified PBO data and PAI data for
*demonstrating the functionality of method COMPARE. PBO data and PAI
*data as well as the results are displayed as ALV lists.
*The interface of method TEST contains three IMPORTING parameters
*(flags) for simulating PAI data that contain new, modified or deleted
*entries and any combination thereof.
zcl_sdn_itab_comparison=>test( id_insert = p_ins
id_update = p_upd
id_delete = p_del ).


END-OF-SELECTION.
The following coding shows how to call method COMPARE. Sorting of the PAI and PBO data is crucial for correct comparison.
DATA:
gt_pbo TYPE STANDARD TABLE OF knb1, " PBO data (old)
gt_pai TYPE STANDARD TABLE OF knb1, " PAI data (new)
gt_insert TYPE STANDARD TABLE OF knb1, " new entries
gt_update TYPE STANDARD TABLE OF knb1, " changed entries
gt_delete TYPE STANDARD TABLE OF knb1. " deleted entries" NOTE: all itab's have the same line type. " No need to define additional structures for data comparison * Sorting is crucial for comparison!!!
SORT gt_pai BY kunnr bukrs.
SORT gt_pbo BY kunnr bukrs.
CALL METHOD zcl_sdn_itab_comparison=>compare
EXPORTING
it_itab_new = gt_pai
it_itab_old = gt_pbo
IMPORTING
et_insert = gt_insert
et_update = gt_update
et_delete = gt_delete.
Method COMPARE takes advantage of the RTTI (Run-Time Type Identification) in order to generated the structures required for data comparison (see documentation of function module CHANGEDOCUMENT_PREPARE_TABLES). These structures include the original structure yet have an additional field for taking up the change indicator ('I' = insert, 'U' = update, 'D' = delete) returned by the function module.
Using method COMPARE developers no longer need to define these additional structures for data comparison because this logic is encapsulated in the method. In addition, the method already evaluates the change indicators and returns three separated internal tables for new, changed and deleted records.

*&---------------------------------------------------------------------*
*& Include Z_SDN_ITAB_COMPARISON_C01
*&---------------------------------------------------------------------*



*---------------------------------------------------------------------*
* CLASS zcl_sdn_itab_comparison DEFINITION
*---------------------------------------------------------------------*
*
*---------------------------------------------------------------------*
CLASS zcl_sdn_itab_comparison DEFINITION.

PUBLIC SECTION.
TYPE-POOLS: abap.

CLASS-METHODS:
" compare two identical itabs (old vs. new; PBO vs. PAI)
compare
IMPORTING
value(it_itab_new) TYPE table " itab with current data (PAI)
value(it_itab_old) TYPE table " itab with old data (PBO)
EXPORTING
et_insert TYPE table " itab with new data
et_update TYPE table " itab with changed data
et_delete TYPE table " itab with deleted data

EXCEPTIONS
error " itabs have different line types
function_call_error, " error when calling
" CHANGEDOCUMENT_PREPARE_TABLES


" test method for COMPARE method
test
IMPORTING
id_insert TYPE abap_bool OPTIONAL
id_update TYPE abap_bool OPTIONAL
id_delete TYPE abap_bool OPTIONAL.

ENDCLASS. "zcl_sdn_itab_comparison DEFINITION








*---------------------------------------------------------------------*
* CLASS zcl_sdn_itab_comparison IMPLEMENTATION
*---------------------------------------------------------------------*
*
*---------------------------------------------------------------------*
CLASS zcl_sdn_itab_comparison IMPLEMENTATION.

METHOD compare.
* The static method COMPARE contains two IMPORTING parameters
* corresponding to the internal tables storing the PAI data (new) and
* the PBO data (old), respectively. The results of the comparison are
* exported as internal tables having the same line type
* as the PBO/PAI data.

* define local data
DATA:
ld_struc_old TYPE string,
ld_struc_new TYPE string,
ld_tabname TYPE tabname,
ldo_new TYPE REF TO data,
ldo_old TYPE REF TO data,
*
ldo_struc TYPE REF TO data,
ldo_di_struc TYPE REF TO data,
ldo_di_new TYPE REF TO data,
ldo_di_old TYPE REF TO data,
*
ldo_insert TYPE REF TO data,
ldo_update TYPE REF TO data,
ldo_delete TYPE REF TO data.


TYPES: BEGIN OF ty_s_di.
TYPES: chind TYPE bu_chind. " change indicator
TYPES: END OF ty_s_di.
DATA:
ls_chind TYPE ty_s_di.


TYPES: BEGIN OF ty_s_di.
TYPES: chind TYPE bu_chind. " change indicator
TYPES: END OF ty_s_di.
DATA:
ls_chind TYPE ty_s_di.


DATA:
lt_components TYPE cl_abap_structdescr=>component_table,
lt_components_di TYPE cl_abap_structdescr=>component_table,
ls_component LIKE LINE OF lt_components,
lo_tab_new TYPE REF TO cl_abap_tabledescr,
lo_tab_old TYPE REF TO cl_abap_tabledescr,
lo_tab_di TYPE REF TO cl_abap_tabledescr,
lo_strucdescr TYPE REF TO cl_abap_structdescr,
lo_typedescr TYPE REF TO cl_abap_typedescr,
lt_tabkey TYPE abap_keydescr_tab.

FIELD-SYMBOLS:
TYPE bu_chind,
*
TYPE ANY,
TYPE ANY,
*
TYPE table,
TYPE table.



* Get RTTI of new itab
lo_tab_new ?= cl_abap_typedescr=>describe_by_data( it_itab_new ).
lo_strucdescr ?= lo_tab_new->get_table_line_type( ).
ld_struc_new = lo_strucdescr->get_relative_name( ).
ld_tabname = ld_struc_new. " type conversion for function module

* Get RTTI of old itab
lo_tab_old ?= cl_abap_typedescr=>describe_by_data( it_itab_old ).
lo_strucdescr ?= lo_tab_old->get_table_line_type( ).
ld_struc_old = lo_strucdescr->get_relative_name( ).

IF ( ld_struc_old NE ld_struc_new ).
RAISE error. " itab's have different line types
ENDIF.


* Create variable having line type of new/old itab
CREATE DATA ldo_struc TYPE HANDLE lo_strucdescr.
ASSIGN ldo_struc->* TO . " line type of new/old itab


* Get components of new/old itab and add component CHIND
lt_components_di = lo_strucdescr->get_components( ).

REFRESH: lt_components.
CLEAR: ls_chind.
lo_strucdescr ?= cl_abap_typedescr=>describe_by_data( ls_chind ).
lt_components = lo_strucdescr->get_components( ).
APPEND LINES OF lt_components TO lt_components_di.



* Create variable having line type of new/old itab with additional
* change indicator field & corresponding itab
lo_strucdescr = cl_abap_structdescr=>create( lt_components_di ).
lo_tab_di = cl_abap_tabledescr=>create( lo_strucdescr ).

CREATE DATA ldo_di_struc TYPE HANDLE lo_strucdescr.
CREATE DATA ldo_di_new TYPE HANDLE lo_tab_di.
CREATE DATA ldo_di_old TYPE HANDLE lo_tab_di.
*
ASSIGN ldo_di_struc->* TO .
ASSIGN ldo_di_new->* TO .
ASSIGN ldo_di_old->* TO .



* Shuffle data from new itab into corresponding itab
* with change indicator (field CHIND)
LOOP AT it_itab_new INTO .
MOVE-CORRESPONDING TO .
APPEND TO .
ENDLOOP.
* Shuffle data from old itab into corresponding itab
* with change indicator (field CHIND)
LOOP AT it_itab_old INTO .
MOVE-CORRESPONDING TO .
APPEND TO .
ENDLOOP.

* NOTE: If check_indicator = ' ' then the itab's are condensed meaning
* that identical entries are removed from both itab's.
* Remaining entries in table_new have the following change indicators:
* - 'I' = INSERT, i.e. a new entry
* - 'U' = UPDATE, i.e. a modified entry
*
* Remaining entries in table_old have the following change indicators:
* - 'D' = DELETE, i.e. a deleted entry
* - ' ' = has a corresponding entry in table_new with CHIND = 'U'
CALL FUNCTION 'CHANGEDOCUMENT_PREPARE_TABLES'
EXPORTING
check_indicator = abap_false
tablename = ld_tabname
* IMPORTING
* RESULT =
TABLES
table_new =
table_old =
EXCEPTIONS
nametab_error = 1
wrong_structure_length = 2
OTHERS = 3.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
RAISING function_call_error.
ENDIF.



* Fill the output itab's depending on the change indicator
* of the records
LOOP AT INTO . " new itab -> INSERT & UPDATE
MOVE-CORRESPONDING TO .
ASSIGN COMPONENT 'CHIND' OF STRUCTURE TO .

CASE .
* New entry (INSERT)
WHEN 'I'.
APPEND TO et_insert.

* Modified entry (UPDATE)
WHEN 'U'.
APPEND TO et_update.

* should not occur
WHEN OTHERS.
CONTINUE.
ENDCASE.

ENDLOOP.

LOOP AT INTO . " old itab -> DELETE
MOVE-CORRESPONDING TO .
ASSIGN COMPONENT 'CHIND' OF STRUCTURE TO .

CASE .
* Delete entry (DELETE)
WHEN 'D'.
APPEND TO et_delete.

* Modified entry (old values)
WHEN OTHERS.
CONTINUE.
ENDCASE.

ENDLOOP.


ENDMETHOD. "compare




METHOD test.
*The static method TEST generates simplified PBO data and PAI data for
*demonstrating the functionality of method COMPARE. PBO data and PAI
*data as well as the results are displayed as ALV lists.
*The interface of method TEST contains three IMPORTING parameters
*(flags) for simulating PAI data that contain new, modified or deleted
*entries and any combination thereof.

* define local data
DATA:
ld_gridtitle TYPE lvc_title,
ls_knb1 TYPE knb1,
lt_knb1_old TYPE STANDARD TABLE OF knb1,
lt_knb1_new TYPE STANDARD TABLE OF knb1,
*
lt_knb1_ins TYPE STANDARD TABLE OF knb1,
lt_knb1_upd TYPE STANDARD TABLE OF knb1,
lt_knb1_del TYPE STANDARD TABLE OF knb1.



* Create 4 sample entries
DO 4 TIMES.
ls_knb1-bukrs = '1000'.
ls_knb1-kunnr = syst-index.
ls_knb1-loevm = abap_false.

APPEND ls_knb1 TO lt_knb1_new.
APPEND ls_knb1 TO lt_knb1_old.
ENDDO.
* NOTE: itab's are identical.


* Sorting is crucial for comparison!!!
SORT lt_knb1_new BY kunnr bukrs.
SORT lt_knb1_old BY kunnr bukrs.



* Delete first entry from old itab => first entry of new itab
* should be inserted (customer = '1' -> CHIND = 'I').
IF ( id_insert = abap_true ).
DELETE lt_knb1_old INDEX 1.
ENDIF.

* Modify second entry of new itab =>
* should be updated (customer = '2' -> CHIND = 'U')
IF ( id_update = abap_true ).
ls_knb1-loevm = abap_true.

MODIFY lt_knb1_new FROM ls_knb1 INDEX 2
TRANSPORTING loevm.
ENDIF.

* Delete third entry from new itab =>
* should be deleted (customer = '3' -> CHIND = 'D').
IF ( id_delete = abap_true ).
DELETE lt_knb1_new INDEX 3.
ENDIF.
* NOTE: customer = '4' is identical in old and new itab => ignored


* Display "old" itab
ld_gridtitle = text-old. " PBO: old itab entries
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
i_structure_name = 'KNB1'
i_grid_title = ld_gridtitle
TABLES
t_outtab = lt_knb1_old
EXCEPTIONS
program_error = 1
OTHERS = 2.
* Display "new" itab
ld_gridtitle = text-new. " PAI: new itab entries
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
i_structure_name = 'KNB1'
i_grid_title = ld_gridtitle
TABLES
t_outtab = lt_knb1_new
EXCEPTIONS
program_error = 1
OTHERS = 2.



* Compare old vs. new itab
CALL METHOD compare
EXPORTING
it_itab_new = lt_knb1_new
it_itab_old = lt_knb1_old
IMPORTING
et_insert = lt_knb1_ins
et_update = lt_knb1_upd
et_delete = lt_knb1_del.



* Display new entries
ld_gridtitle = text-ins. " INSERT: new entries
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
i_structure_name = 'KNB1'
i_grid_title = ld_gridtitle
TABLES
t_outtab = lt_knb1_ins
EXCEPTIONS
program_error = 1
OTHERS = 2.
* Display changed entries
ld_gridtitle = text-upd. " UPDATE: changed entries
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
i_structure_name = 'KNB1'
i_grid_title = ld_gridtitle
TABLES
t_outtab = lt_knb1_upd
EXCEPTIONS
program_error = 1
OTHERS = 2.
* Display deleted entries
ld_gridtitle = text-del. " DELETE: deleted entries
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY_LVC'
EXPORTING
i_structure_name = 'KNB1'
i_grid_title = ld_gridtitle
TABLES
t_outtab = lt_knb1_del
EXCEPTIONS
program_error = 1
OTHERS = 2.

ENDMETHOD. "test

ENDCLASS. "zcl_sdn_itab_comparison IMPLEMENTATION

No comments:

Post a Comment