Daten dynamisch verwalten

Daten werden in der Regel in Tabellen abgelegt. Bei komplexen Datenstrukturen oder auch bei Daten, die nicht allzulange vorgehalten werden müssen, bietet sich ein EXPORT TO DATABASE an. Zusammen mit der Umwandlung von Daten ins XML-Format kann man eine Menge interessanter Dinge machen. In diesem Beispiel Speichern wir eine Tabelle mit einer bestimmten Struktur und lesen die Daten danach wieder in eine Tabelle mit einer anderen Struktur.

Um was geht es?

Wir simulieren in diesem Beispiel eine Strukturänderung, die in Folge einer Programmerweiterung auftreten kann.

Stellen Sie sich vor, Sie haben eine Struktur in der Sie die Materialnummer und eine Serialnummer zu einem “Vorgang” speichern.

Die Struktur sähe dann z.B. so aus:

TYPES: BEGIN OF ty_struc,
         vorgang TYPE c LENGTH 10,
         matnr   TYPE c LENGTH 18,
         sernr   TYPE c LENGTH 18,
       END OF ty_struc.

Die Speicherung erfolgt durch EXPORT TO DATABASE:

EXPORT gt_data TO DATABASE indx(zz) ID ‘$mydata$’.

Das Programm läuft und funktioniert gut. Es werden zu einem Vorgang Materialnummer und Serialnummer gespeichert und gelesen.

Strukturänderung

Nach einiger Zeit erweist es sich jedoch als hilfreich, die Equipmentnummer zum Vorgang ebenfalls zu speichern. Sie fügen den Feldern VORGANG, MATNR und SERNR also das Feld EQUNR hinzu:

TYPES: BEGIN OF ty_struc,
         vorgang TYPE c LENGTH 10,
         equnr   TYPE c LENGTH 18,
         matnr   TYPE c LENGTH 18,
         sernr   TYPE c LENGTH 18,
       END OF ty_struc.

Wenn Sie nun allerdings einen alten Vorgang bearbeiten, werden Sie einen Shortdump bekommen:

Laufzeitfehler         CONNE_IMPORT_WRONG_COMP_LENG
Ausnahme               CX_SY_IMPORT_MISMATCH_ERROR

Die Zusätze IGNORING STRUCTURE BOUNDARIES oder IGNORING CONVERSION CONVERSION ERRORS helfen uns leider nicht weiter.

Die Lösung

Man kann einfach Abhilfe schaffen, in dem man von Anfang an die Daten in ein XML-Objekt umwandelt. Das geht mithilfe von CALL TRANSFORMATION extrem schnell, unkompliziert und sicher.

CALL TRANSFORMATION id 
     SOURCE data = gt_data
     RESULT XML xml_string.

Der Trick dabei ist, dass im Datencluster nicht vorhandene Felder einfach ignoriert werden. Ebenso werden Felder ignoriert, die zwar im Datencluster, aber nicht in der zu importierenden Struktur/ Tabelle vorhanden sind.

Die Struktur kann fast beliebig geändert werden, solange die Feldnamen identisch bleiben. Sogar Feldtypen können problemlos geändert werden.

Wenn natürlich ein CHAR-Feld in ein INT-Feld geändert wird, dann kann ein C-Wert natürlich nicht in ein numerisches Feld gewandelt werden.

Und wozu brauche ich das?

Die Anwendung ist sicherlich nicht ganz alltäglich, denn normalerweise wird man wohl eine Datenbank-Tabelle definieren und die Daten dort strukturiert ablegen.

Interessant wird es, wenn die Daten zu einer Anwendung teilweise dynamisch – zum Beispiel unterschiedliche Felder je Belegart – verwaltet werden. Sie könnten dann per Customizing zusätzliche Felder definieren, die in einer Anwendung geändert werden sollen. Mittels RTTI ist es inzwischen einfach, anhand eines Feldkatalogs eine interne Tabelle zu generieren.

siehe: Interne Tabelle zur Laufzeit generieren

Zur Anzeige oder Änderung kann dann ein ALV-Grid verwendet werden. Wenn Sie diese Art der Datenspeicherung verwenden, dann kann die Tabellenstruktur fast beliebig geändert werden.

Coding

Das folgende Programm demonstriert das Vorgehen und simuliert die zeitliche Änderung einer Struktur dadurch, das einfach zwei verschiedene Strukturen verwendet werden.

*== old structure
TYPES: BEGIN OF ty_test1,
         eins TYPE c LENGTH 2,
         zwei TYPE c LENGTH 2,
         drei TYPE c LENGTH 2,
       END OF ty_test1.

*== new structure
TYPES: BEGIN OF ty_test2,
         neu TYPE c LENGTH 2,
         eins TYPE c LENGTH 5,
         zwei TYPE i,
       END OF ty_test2.

*== data tables
DATA gt_test1 TYPE STANDARD TABLE OF ty_test1.
DATA gt_test2 TYPE STANDARD TABLE OF ty_test2.
*== id for storing data
DATA gc_id TYPE c LENGTH 20 VALUE '$TRICKTRESOR!'.
*== XML-String containing the data in XML-format
DATA gv_xml TYPE string.
*== exception reference CALL TRANSFORMATION
DATA gx_error TYPE REF TO cx_dynamic_check.

START-OF-SELECTION.

*== add test data to "old" table
  APPEND 'AA1111' TO gt_test1.
  APPEND 'BB2222' TO gt_test1.
  APPEND 'CC3333' TO gt_test1.

*== save xml string into data cluster*== Transform data table into xml string
CALL TRANSFORMATION id
     SOURCE test = gt_test1
     RESULT XML gv_xml.

EXPORT test = gv_xml TO DATABASE indx(zv) ID gc_id.

*== clearance
CLEAR gt_test1.
CLEAR gt_test2.
CLEAR gv_xml.

*== import data into xml string
IMPORT test TO gv_xml FROM DATABASE indx(zv) ID gc_id.

TRY .
*== transform xml data into table with NEW structure
  CALL TRANSFORMATION id
       SOURCE XML gv_xml
       RESULT test = gt_test2.
    CATCH cx_transformation_error INTO gx_error.
  BREAK-POINT.
ENDTRY.

Enno Wulff

Leave a Comment