Eine über­ra­schende Lösung schwer­wie­gen­der Kon­sis­tenz-Pro­bleme mit Imple­men­tie­rungs-Bei­spie­len aus IBM InfoS­phere DataStage

Ein­lei­tung

SAP ent­wi­ckelt markt­füh­rende Enter­prise-Resource-Plan­ning-Sys­teme (kurz: ERP-Sys­teme), die zur Abbil­dung und Ver­wal­tung gesamt­be­trieb­li­cher Pro­zesse genutzt wer­den kön­nen. Eines der SAP Module heißt SAP S/4HANA for Finan­cial Pro­ducts Sub­le­ger (kurz: SAP FPSL) und wird für die Buch­hal­tung kom­ple­xer Finanz­pro­dukte von Ban­ken und Ver­si­che­run­gen ein­ge­setzt. Mit InfoS­phere Data­S­tage stellt IBM eines der füh­ren­den Soft­ware-Pro­dukte zur Imple­men­tie­rung von Extract-Trans­form-Load (kurz: ETL) Pro­zes­sen im Kon­text von Data Ware­housing und Data Inte­gra­tion bereit.

Die Ver­bin­dung zwi­schen der ETL-Welt und SAP FPSL kann über das SAP Busi­ness Appli­ca­tion Pro­gramming Inter­face (kurz: BAPI) her­ge­stellt wer­den. Mit Hilfe die­ser stan­dar­di­sier­ten Schnitt­stelle kön­nen externe Kom­po­nen­ten die vor­han­de­nen Daten im SAP FPSL lesen und neue Daten ins SAP FPSL hin­zu­fü­gen. IBM hat hier­für mit der soge­nann­ten BAPI Stage einen von SAP zer­ti­fi­zier­ten Kon­nek­tor ent­wi­ckelt, wel­cher Daten direkt aus einem ETL-Pro­zess her­aus ent­ge­gen­neh­men kann, um diese nach SAP FPSL zu über­mit­teln. Dabei kön­nen ins­be­son­dere im Zusam­men­spiel mit kom­ple­xe­ren Daten schnell Kon­sis­tenz-Pro­bleme in SAP FPSL entstehen.

In die­sem Bei­trag durch­leuch­ten wir, wie diese Inkon­sis­ten­zen zustande kom­men und prä­sen­tie­ren eine über­ra­schende Lösung, mit wel­cher wir die Daten sta­bil über­mit­teln kön­nen. Abschlie­ßend möch­ten wir anhand eines Bei­spiels eine mög­li­che Imple­men­tie­rung in Data­S­tage für die Lösung zei­gen. Doch zunächst begin­nen wir im fol­gen­den Abschnitt mit einigen

Kom­plexe Datenstrukturen

Daten­struk­tu­ren in SAP

In unse­rem Anwen­dungs­fall möch­ten wir diverse Trans­ak­tio­nen wie Prä­mien und Scha­dens­zah­lun­gen aus unter­schied­li­chen Quel­len nach SAP FPSL lie­fern. Die Quell­sys­teme sind dabei tabel­la­risch auf­ge­baut und lie­fern für jede Trans­ak­tion genau eine Zeile aus der ent­spre­chen­den Transaktionstabelle.

Das BAPI hin­ge­gen erwar­tet beim Auf­ruf eini­ger Metho­den neben nor­ma­len Eigen­schafts­fel­dern auch struk­tu­rierte Daten zu jedem über­tra­ge­nen Daten­satz. Diese Sub­struk­tu­ren haben tabel­len­ar­tige Eigen­schaf­ten, die über fremd­schlüs­sel­ähn­li­che Kon­strukte logisch mit­ein­an­der ver­knüpft sein können.

Gene­rell kön­nen über die soge­nannte BAPI Sequenz Num­mer viele Daten­satze als gebün­del­tes Paket nach SAP über­tra­gen wer­den. Für jede neue Num­mer wird ein BAPI Call abge­setzt. Im Fol­gen­den kon­zen­trie­ren wir uns jedoch auf die Methode zur Über­tra­gung der Busi­ness Tran­sac­tions (vgl. Abbil­dung 1).

Die Methode für Busi­ness Transactions

Über die­ses Modul muss für jedes Busi­ness Event (hier: eine Trans­ak­tion) genau ein BAPI Call mit einer ein­deu­ti­gen, noch nicht ver­wen­de­ten BAPI Sequenz Num­mer abge­setzt wer­den. Eine ein­zige Trans­ak­tion kann dabei aller­dings gleich meh­rere Buchun­gen aus­lö­sen. Diese Buchun­gen bezeich­net man als Posi­ti­ons oder als ItemsSAP for­dert sie über eine Tabel­len­struk­tur namens POSITIONS an. Inner­halb die­ser Struk­tur erhal­ten die Items eine je BAPI Sequenz Num­mer hoch­lau­fende Posi­tion Num­mer sowie Aus­prä­gun­gen, die das zu buchende Konto (Tran­sac­tion Type), die Buchungs­rich­tung (Pos­ting Direc­tion), den zu buchen­den Betrag (Amount) und das Buchungs­da­tum (Pos­ting Date) für die­ses Item ausweisen.

Dar­über hin­aus kön­nen im SAP über eine wei­tere tabel­len­ar­tige Sub­struk­tur POSCHARACTERISTICS zu jeder Posi­tion belie­big viele, frei anpass­bare Eigen­schaf­ten über­mit­telt wer­den. Diese auch als Cus­to­mer Added Cha­rac­te­ristics (kurz: CACs) bezeich­ne­ten Name-Wert-Paare wer­den mit einer eige­nen Posi­tion Num­mer ver­se­hen, um sie wie ein Fremd­schlüs­sel mit einem Item in Ver­bin­dung zu setzen.

Grafik 1 BAPI Konnektor in IBM DataStage: Zwei Substrukturen sollen übermittelt werden
Abbil­dung 1 BAPI Kon­nek­tor in IBM Data­S­tage: Zwei Sub­struk­tu­ren sol­len über­mit­telt werden

Im vor­lie­gen­den, kon­kre­ten Bei­spiel hat es zwei Busi­ness Events gege­ben, wel­che jeweils einen BAPI Call aus­lö­sen. Der erste wird über die BAPI Sequenz Num­mer 1001 abge­wi­ckelt, der zweite über die Num­mer 1002 (vgl. Tab 1.1).

Tabelle 1.1 Zwei BAPI Calls
Tabelle 1.1 Zwei BAPI Calls

Das Event 1001 soll dabei im SAP zwei Buchun­gen aus­lö­sen: Die erste über den Tran­sac­tion Type YD5000 mit Buchungs­rich­tung C und Betrag 300; die zweite über YS7300 mit D und 1500. Das Event 1002 hin­ge­gen wird nur eine ein­zige Buchung über den Tran­sac­tion Type YD5000 mit Buchungs­rich­tung D und Betrag 2300 aus­lö­sen (vgl. Tab 1.2).

Tabelle 1.2 Items zu den BAPI Calls
Tabelle 1.2 Items zu den BAPI Calls

Alle drei Buchun­gen sol­len um jeweils drei CACs erwei­tert wer­den. Die Eigen­schaf­ten wie auch deren Werte sind in die­sem Bei­spiel für jedes Item die­sel­ben (vgl. Tab 1.3).

Tabelle 1.3 Customer Added Characteristics
Tabelle 1.3 Cus­to­mer Added Characteristics
Das Pro­blem

Die Daten kön­nen mit­tels des BAPI-Kon­nek­tors nicht in einer kom­plex-struk­tu­rier­ten Form über­mit­telt wer­den. Somit müs­sen wir kom­plexe SAP-Daten­ty­pen wie zum Bei­spiel Tabel­len abfla­chen, sodass wir eine Über­tra­gung auf Zei­len­ba­sis aus­füh­ren können.

Tabelle 2 Inner Join der drei Strukturen
Tabelle 2 Inner Join der drei Strukturen

Diese Form der Daten­be­reit­stel­lung hat zwar den Charme, dass wir sie rela­tiv leicht erzeu­gen kön­nen. Aller­dings zieht sie Inkon­sis­ten­zen im Ziel­sys­tem SAP nach sich, sobald zwei tabel­len­ar­tige Struk­tu­ren mit meh­re­ren Wer­ten belie­fert wer­den sollen.

Ein ers­ter Versuch

Die aus SAP-Sicht struk­tu­rier­ten Daten der POSITIONS (in Tab 2 gelb mar­kiert) und der POSCHARACTERISTICS (grün) wer­den getrennt von­ein­an­der und zei­len­weise ein­ge­le­sen. Sobald inner­halb eines BAPI Calls in zwei auf­ein­an­der­fol­gen­den Zei­len die­sel­ben Werte in den POSITIONS ste­hen, hört der ent­spre­chende Lese­pro­zess für die POSITIONS auf und setzt erst wie­der beim nächs­ten BAPI Call ein. Der Lese­pro­zess für die POSCHARACTERISTICS ver­läuft analog.

In unse­rem Bei­spiel stoppt der Lese­pro­zess der POSITIONS bereits nach der ers­ten Zeile, weil in der zwei­ten Zeile das­selbe Item erneut auf­ge­führt ist. Erst mit einem neuen BAPI Call für das Event mit der Num­mer 1002 setzt der Lese­pro­zess wie­der ein, sodass das zweite Item der BAPI Sequenz Num­mer 1001 nicht über­tra­gen wird (vgl. Tab 3). Eine Feh­ler­mel­dung dies­be­züg­lich wird nicht ausgegeben.

Die CACs hin­ge­gen wer­den voll­stän­dig ein­ge­le­sen und ins Ziel­sys­tem über­tra­gen, da mit jedem Zei­len­wech­sel neue Daten erfasst wer­den. Dies wirft eine Feh­ler­mel­dung, da die Cus­to­mer Added Cha­rac­te­ristics mit Item Num­mer 2 kei­nem Item zuge­ord­net wer­den können.

Wenn die Daten­sätze auf andere Weise sor­tiert sind, als in Tabelle 2 dar­ge­stellt, so ist es durch­aus mög­lich, zumin­dest einen Teil der Daten zufäl­li­ger­weise kor­rekt und voll­stän­dig zu über­tra­gen. Um eine wirk­lich sta­bile Über­tra­gung zu errei­chen bedarf es aller­dings ab zwei Sub­struk­tu­ren einer ande­ren Lösung als den hier vor­ge­stell­ten Inner Join.

Der SAP-Lese­al­go­rith­mus

Die aus SAP-Sicht struk­tu­rier­ten Daten der POSITIONS (in Tab 2 gelb mar­kiert) und der POSCHARACTERISTICS (grün) wer­den getrennt von­ein­an­der und zei­len­weise ein­ge­le­sen. Sobald inner­halb eines BAPI Calls in zwei auf­ein­an­der­fol­gen­den Zei­len die­sel­ben Werte in den POSITIONS ste­hen, hört der ent­spre­chende Lese­pro­zess für die POSITIONS auf und setzt erst wie­der beim nächs­ten BAPI Call ein. Der Lese­pro­zess für die POSCHARACTERISTICS ver­läuft analog.

In unse­rem Bei­spiel stoppt der Lese­pro­zess der POSITIONS bereits nach der ers­ten Zeile, weil in der zwei­ten Zeile das­selbe Item erneut auf­ge­führt ist. Erst mit einem neuen BAPI Call für das Event mit der Num­mer 1002 setzt der Lese­pro­zess wie­der ein, sodass das zweite Item der BAPI Sequenz Num­mer 1001 nicht über­tra­gen wird (vgl. Tab 3). Eine Feh­ler­mel­dung dies­be­züg­lich wird nicht ausgegeben.

Die CACs hin­ge­gen wer­den voll­stän­dig ein­ge­le­sen und ins Ziel­sys­tem über­tra­gen, da mit jedem Zei­len­wech­sel neue Daten erfasst wer­den. Dies wirft eine Feh­ler­mel­dung, da die Cus­to­mer Added Cha­rac­te­ristics mit Item Num­mer 2 kei­nem Item zuge­ord­net wer­den können.

Wenn die Daten­sätze auf andere Weise sor­tiert sind, als in Tabelle 2 dar­ge­stellt, so ist es durch­aus mög­lich, zumin­dest einen Teil der Daten zufäl­li­ger­weise kor­rekt und voll­stän­dig zu über­tra­gen. Um eine wirk­lich sta­bile Über­tra­gung zu errei­chen bedarf es aller­dings ab zwei Sub­struk­tu­ren einer ande­ren Lösung als den hier vor­ge­stell­ten Inner Join.

Tabelle 3 Der Einleseprozess der POSITIONS verpasst das zweite Item zur BAPI Sequenz Nummer 1001
Tabelle 3 Der Ein­le­se­pro­zess der POSITIONS ver­passt das zweite Item zur BAPI Sequenz Num­mer 1001

Die Lösung

Eine kon­sis­tente Anlie­fe­rung der Daten in das Ziel­sys­tem SAP kön­nen wir nur dann errei­chen, wenn wir die Daten für den BAPI-Kon­nek­tor so auf­be­rei­ten, dass die Lese­al­go­rith­men sämt­li­che Daten­sätze erfas­sen. Wir beschrei­ben die Ziel­struk­tur zunächst anhand unse­res Bei­spiels (vgl. Tab 4) und ent­wi­ckeln dar­auf auf­bau­end einen recht abs­trak­ten, dafür aber all­ge­mein­gül­ti­gen Ansatz.

Die Lösung anhand des Beispiels

Da wir sechs ver­schie­dene Cus­to­mer Added Cha­rac­te­ristics für die BAPI Sequenz Num­mer 1001 über­mit­teln möch­ten, muss die­ser BAPI Call sechs Zei­len erhal­ten. Die BAPI Sequenz Num­mer wird darin stets wie­der­holt, die CACs erhal­ten je eine Zeile. Bei den POSITIONS füh­ren wir in der ers­ten Zeile das erste Item und in allen fol­gen­den fünf Zei­len das zweite Item auf. Für die BAPI Sequenz Num­mer 1002 sol­len drei CACs gesen­det wer­den. Wir gene­rie­ren also drei Zei­len, wie­der­ho­len stets die BAPI Sequenz Num­mer und plat­zie­ren je Zeile einen CAC. Da wir für die 1002 nur ein Item schi­cken, wie­der­ho­len wir die­ses in jeder der drei Zeilen.

Tabelle 4 Neue Struktur für die Anlieferung der Daten in Richtung BAPI-Konnektor
Tabelle 4 Neue Struk­tur für die Anlie­fe­rung der Daten in Rich­tung BAPI-Konnektor
All­ge­mein­gül­tige Lösung

Um ein sol­ches Ziel­for­mat all­ge­mein­gül­tig zu beschrei­ben, füh­ren wir zuerst einige Varia­blen ein.

Varia­bleDefi­ni­tionWert im Beispiel*
iAnzahl aller Sub­struk­tu­ren für einen BAPI Call2
m(k), für 1<=k<=iDie Anzahl der Daten­sätze, die wir für die k‑te Sub­struk­tur über­tra­gen möchtenm(1) = 2
m(2) = 6
nn = max(m(k), mit 1<=k<=i)

Die maxi­male Anzahl an Daten­sät­zen, die wir für einen BAPI Call inner­halb einer Struk­tur über­trage möchten
6

* für die BAPI Sequenz Num­mer 1001; mit Sub­struk­tur 1 = POSITIONS und Sub­struk­tur 2 = POSCHARACTERISTICS

Mit die­sen Defi­ni­tio­nen kann das Ziel­for­mat für eine BAPI Sequenz Num­mer wie folgt aussehen:

  • Erstelle n Zeilen
  • Wie­der­hole BAPI Sequenz Num­mer sowie alle wei­te­ren ein­fa­chen Para­me­ter (in unse­rem Bei­spiel das Quell­sys­tem) in jeder Zeile der n Zeilen
  • Für Spal­ten, die im SAP die Sub­struk­tur k wider­spie­geln, gilt: Trage die Daten­sätze in die Zei­len 1 bis m(k) Wie­der­hole den m(k)-ten Daten­satz in den Zei­len m(k)+1 bis n

Umset­zung in DataStage

Nach­dem wir in Abschnitt 4 gezeigt haben, wie wir die Daten zur kon­sis­ten­ten Belie­fe­rung der BAPI Stage struk­tu­rie­ren müs­sen, möch­ten wir nun abschlie­ßend anhand eines Bei­spiels demons­trie­ren, wie wir die­ses Daten­for­mat inner­halb eines Data­S­tage Jobs erzeu­gen kön­nen (vgl. Abbil­dung 2).

Job­de­sign

Wir gehen davon aus, dass wir die Busi­ness Events aus einer Daten­bank ein­le­sen kön­nen (SHTa­bell­e­Le­sen) und ihnen in einem zwei­ten Schritt die gewünsch­ten Items inklu­sive einer pas­sen­den Posi­tion Num­mer zuwei­sen kön­nen (SHI­tems). Nach die­ser Vor­be­rei­tung geschieht der alles ent­schei­dende Trick im drit­ten Schritt: In dem Shared Con­tai­ner SHCAC gene­rie­ren wir nicht nur die Cus­to­mer Added Cha­rac­te­ristics, son­dern struk­tu­rie­ren eben­falls die Daten wie in Tabelle 4 beschrie­ben. Abschlie­ßend map­pen wir die Daten auf das Ziel­for­mat des BAPI-Kon­nek­tors, sor­tie­ren und schi­cken sie über die BAPI-Stage nach SAP FPSL.

Grafik 2 Parallel Job Design in IBM DataStage
Abbil­dung 2 Par­al­lel Job Design in IBM DataStage 
Shared Con­tai­ner CAC

Wir erzeu­gen die CACs abseits des regu­lä­ren Daten­stroms. Dabei erhal­ten sie nicht nur ihre eigene Posi­tion Num­mer (ITEM_NO POSCHAR), son­dern auch die Posi­tion Num­mer des Items (ITEM_NO POS), mit dem sie zwar nicht zwin­gend logisch, aber den­noch struk­tu­rell über einen Join ver­bun­den wer­den sollen.

Grafik 3 Shared Container SHCAC zur Erzeugung der Customer Added Characteristics
Abbil­dung 3 Shared Con­tai­ner SHCAC zur Erzeu­gung der Cus­to­mer Added Characteristics 

Im Ein­gang die­ses Shared Con­tai­ners sehen die Daten aus wie in Tabelle 5.1 gezeigt: Für jedes Item gibt es genau einen Daten­satz, in dem alle Infor­ma­tio­nen für die­ses Item gespei­chert sind.

Tabelle 5.1 Daten vor dem Transformer TRA_Split
Tabelle 5.1 Daten vor dem Trans­for­mer TRA_Split
Der Trans­for­mer TRA_Split

Der erste zen­trale Bau­stein ist der Trans­for­mer TRA_Split (vgl. Abbil­dung 4). Über den Link LN_Data_Stream_zu_Join_Left lei­tet die­ser sämt­li­che Spal­ten der Daten mit­tels Run­time Column Pro­pa­ga­tion (kurz: RCP) an die fol­gende Join Stage JOI_Join wei­ter. Über den zwei­ten Link LN_zu_CAC hin­ge­gen lei­tet der Trans­for­mer nur aus­ge­wählte Spal­ten und auch nur sol­che Zei­len, die inner­halb einer BAPI Sequenz Num­mer die höchste Posi­tion Num­mer inne­ha­ben. Dafür sor­tie­ren wir vor dem Trans­for­mer mit SOR_Split nach der BAPI­Se­quenz­Num­mer und nut­zen im Trans­for­mer den Fil­ter LastRowInGroup(LN_von_SOR.BAPI­Se­quenz­Num­mer). Tabelle 5.2 stellt die Daten an die­ser Stelle dar.

Tabelle 5.2 Daten vor dem Transformer TRA_CAC
Tabelle 5.2 Daten vor dem Trans­for­mer TRA_CAC
Grafik 4 Transformer Stage TRA_Split
Abbil­dung 4 Trans­for­mer Stage TRA_Split
Der Trans­for­mer TRA_CAC

Im wei­te­ren Ver­lauf erzeu­gen wir die CACs über einen Loop in der Trans­for­mer Stage TRA_CAC (vgl. Abbil­dung 5). Zu jedem ein­tref­fen­den Daten­satz erstel­len wir in unse­rem Bei­spiel NumChar x MAX_PositionNumber CACs. Dabei ist NumChar = 3 die Anzahl der ver­schie­de­nen CACs und MAX_PositionNumber die höchste Posi­tion Num­mer zur aktu­el­len BAPI Sequenz Num­mer. Wir set­zen den Namen (Spalte: CUSTOMER_ADDED_CHARACTERISTICS), den Wert (Spalte: FIELD_VALUE) und berech­nen die zuge­hö­rige Posi­tion Num­mer, wel­che den aktu­el­len CAC mit einem Item logisch ver­knüp­fen soll (Spalte: PositionNummer_CAC), indem wir bei 1 star­ten und nach jedem Durch­lauf über die gesamte Liste der CACs (dann gilt: Mod(@ITERARION‑1, NumChar) = 0) um 1 erhö­hen. So erhal­ten die ers­ten drei CACs die Posi­tion Num­mer 1, die nächs­ten drei erhal­ten die 2, usw.

Dar­über hin­aus ermit­teln wir für jeden CAC die Posi­tion Num­mer des Items, mit wel­chem der CAC struk­tu­rell ver­bun­den wer­den soll (Spalte: Posi­ti­onNum­mer). Hierzu star­ten wir eben­falls bei 1, zäh­len aller­dings bei jeder Ite­ra­tion um 1 hoch bis wir die maxi­male Posi­tion Num­mer für die aktu­ell BAPI Sequenz Num­mer erreicht haben und wie­der­ho­len diese fortan. Tabelle 5.3 zeigt das Ergeb­nis des Trans­for­mers TRA_CAC.

Tabelle 5.3 Daten nach dem Transformer TRA_CAC
Tabelle 5.3 Daten nach dem Trans­for­mer TRA_CAC
Grafik 5 Transformer Stage TRA_CAC
Abbil­dung 5 Trans­for­mer Stage TRA_CAC
Der Join JOI_Join

In der abschlie­ßen­den Join Stage JOI_Join hef­ten wir die erzeug­ten CACs über einen Inner Join mit den Schlüs­seln BAPI­Se­quenz­Num­mer und Posi­ti­onNum­mer an die ursprüng­li­chen Daten­sätze des Links LN_Data_Stream_zu_Join_Left (vgl. Abbil­dung 6) und erhal­ten so auf all­ge­mein­gül­tige Weise das For­mat der Tabelle 4.

Grafik 6 Join Stage JOI_Join
Abbil­dung 6 Join Stage JOI_Join

Mit die­ser doch recht spe­zi­el­len Bela­dungs­tech­nik stel­len wir letzt­lich sicher, dass der SAP-Lese­al­go­rith­mus alle über das BAPI gesen­de­ten Daten erfasst und wie gewünscht mit­ein­an­der ver­knüp­fen kann. Wir konn­ten die wei­ter oben beschrie­be­nen Kon­sis­tenz­pro­bleme auflösen.