• Abmischen von Zeitscheiben zu Mitarbeiterstammdaten

    2
    0 Stimmen
    2 Beiträge
    35 Aufrufe
    D
    Hallo Jörg, danke nochmals für die Erklärungen und Ergänzungen. Wie besprochen schicke ich noch den Code durch. do begin --Zeitscheiben aufbohren und neu abbilden ueber 3 Iterationen --Iteration 1: vorhandene Zeitscheiben DATEFROM, DATETO auf einzelne Jahre aufbohren (via join) und Grenzen neu setzen --Iteration 2: entrydate ueber jahre hochzaehlen, da wo entrydate zwischen DATEFORM und DATETO liegt muss aufgebohrt (via union) und Grenzen neu gesetzt werden --Iteration 3: datebirth ueber jahre hochzaehlen, da wo datebirth zwischen DATEFORM und DATETO liegt muss aufgebohrt (via union) und Grenzen neu gesetzt werden --Basisdaten ziehen lt_01 = select "/BIC/ZEMPLOYEE" as employee, to_date(datefrom) as datefrom, to_date(dateto) as dateto, to_date(entrydate) as entrydate, to_date(datebirth) as datebirth from "/BIC/MZEMPLOYEE" order by employee, datefrom asc; --Jahre ziehen um Zeitscheiben, die ueber mehrere Jahre gehen, aufzuloesen lt_02 = select CALYEAR from "/BI0/SCALYEAR" order by calyear asc; --Zeitscheiben aufloesen, die ueber mehrere Jahre gehen lt_03 = select employee, datefrom, dateto, entrydate, calyear, datebirth from :lt_01 left join :lt_02 on calyear between year(datefrom ) and year(dateto) -- year(datefrom) and calyear < year(dateto) order by employee, datefrom, calyear asc; --Dateto neu setzen via calyear fuer die aus dem Join neu generierten Saetze lt_031 = select employee, datefrom, dateto, case when year(dateto) > year(datefrom) and year(dateto) <> calyear then to_date(calyear || '1231') else dateto end as dateto2, entrydate, datebirth, calyear from :lt_03 order by employee, datefrom, dateto2; --Vorberechnung als Basis fuer nachste itab (lasst sich nicht in einem Schritt machen) lt_032 = select employee, datefrom, dateto, lag(dateto) over(partition by employee order by employee, datefrom, dateto2 asc) as lag_dateto, dateto2, entrydate, datebirth, calyear from :lt_031 order by employee, datefrom, dateto2; --datefrom und dateto neu setzen aufgrund der neu hinzugekommenen Zeitscheiben lt_04 = select employee, case when lag_dateto = dateto then add_days(lag(dateto2) over(partition by employee order by employee, datefrom, dateto2 asc), 1) else datefrom end as datefrom, case when dateto2 between datefrom and dateto then dateto2 else dateto end as dateto, entrydate, datebirth from :lt_032; --hier noch das entrydate mit dem angepassten Jahr abbilden (Laufjahr) als Basis, um damit die Zeitscheiben auf entrydate aufzusetzen lt_05 = select employee, datefrom, dateto, entrydate, to_date( year(datefrom) || substr(entrydate, 5,9)) as entrydate_new, datebirth, to_date( year(datefrom) || substr(datebirth, 5,9)) as datebirth_new from :lt_04; --wenn entrydate_new zwischen datefrom und dateto liegt, dann muss ein zusaetzlicher satz erzeugt werden, um die range --auf Basis vom entrydate_new neu zu bilden lt_06 = select * from :lt_05 union all select employee, datefrom, dateto, entrydate, entrydate_new, datebirth, datebirth_new from :lt_05 where entrydate_new between datefrom and dateto and not datefrom = entrydate_new --wenn das entrydate fuer einen employee wechselt, dann duerfen diese saetze nicht verdoppelt werden, weil sonst die zeitscheiben --nicht korrekt kommen, da entrydate genau auf datefrom liegt order by employee, datefrom asc; -- lt_07 = select employee, datefrom, case when entrydate_new between datefrom and dateto and lead(datefrom) over(partition by employee order by employee, datefrom) = datefrom then datefrom when entrydate_new not between datefrom and dateto then datefrom else entrydate_new end as datefrom_new, dateto, entrydate, entrydate_new, datebirth, datebirth_new from :lt_06 order by employee, datefrom_new asc; --auf Basis von datefrom_new kann nun auch dateto_new korrekt berechnet werden lt_071 = select employee, datefrom, datefrom_new, dateto, case when entrydate_new between datefrom and dateto and lead(dateto) over(partition by employee, entrydate order by employee, datefrom_new asc) = dateto and not entrydate_new = datefrom then add_days(entrydate_new, -1) else dateto end as dateto_new, entrydate, entrydate_new, datebirth, datebirth_new from :lt_07 order by employee, datefrom_new asc; lt_08 = select employee, datefrom_new as datefrom, dateto_new as dateto, entrydate, years_between( to_date(entrydate), dateto_new) as betriebsz, datebirth, datebirth_new from :lt_071; --ab hier weiter mit Lebensaltersberechnung lt_09 = select employee, datefrom, dateto, entrydate, betriebsz, datebirth, datebirth_new from :lt_08 union all select employee, datefrom, dateto, entrydate, betriebsz, datebirth, datebirth_new from :lt_08 where datebirth_new between datefrom and dateto and not datefrom = datebirth_new --wenn das entrydate fuer einen employee wechselt, dann duerfen diese saetze nicht verdoppelt werden, weil sonst die zeitscheiben --nicht korrekt kommen, da entrydate genau auf datefrom liegt order by employee, datefrom asc; lt_10 = select employee, datefrom, case when datebirth_new between datefrom and dateto and lead(dateto) over(partition by employee, datebirth order by employee, datefrom asc) = dateto then datefrom when datebirth_new not between datefrom and dateto then datefrom else datebirth_new end as datefrom_new, dateto, entrydate, betriebsz, datebirth, datebirth_new from :lt_09; lt_101 = select employee, --datefrom, datefrom_new, --dateto, case when datebirth_new between datefrom and dateto and lead(dateto) over(partition by employee, datebirth order by employee, datefrom_new asc) = dateto and not datebirth_new = datefrom then add_days(datebirth_new, -1) else dateto end as dateto_new, entrydate, datebirth, datebirth_new from :lt_10 order by employee, datefrom_new asc; lt_11 = select employee, datefrom_new, dateto_new, entrydate, datebirth, years_between( to_date(entrydate), dateto_new) as betriebsz, years_between( to_date(datebirth), dateto_new) as alter_kyf from :lt_101; lt_12 = select employee, min(datefrom_new) as datefrom, max(dateto_new) as dateto, entrydate, datebirth, betriebsz, alter_kyf from :lt_11 group by employee, entrydate, datebirth, betriebsz, alter_kyf order by employee, datefrom; select * from :lt_01; select * from :lt_02; select * from :lt_03; select * from :lt_031; select * from :lt_04; select * from :lt_05; select * from :lt_06; select * from :lt_07; select * from :lt_08; select * from :lt_09; select * from :lt_10; select * from :lt_101; select * from :lt_11; select * from :lt_12; end;
  • Semantische Gruppierung mit AMDP unter BW on HANA 7.5

    Verschoben
    2
    0 Stimmen
    2 Beiträge
    16 Aufrufe
    JörgJ
    Hallo, vielen Dank für Deine Frage. Hatte ich tatsächlich kurz angesprochen. Aber auf unserer Website habe ich auch mal einen kurzen Artikel dazu geschrieben: https://www.brandeis.de/blog/2024/semantic-grouping-in-bw75 Hier der Artikel: Leider funktioniert im BW 7.50 bei der HANA-Ausführung die semantische Gruppierung nicht, um damit alle Datensätze mit gleichen Teilschlüssel im gleichen Paket zu haben. Das ist aber manchmal notwendig, wenn in unseren Routinen Berechnungen durchgeführt werden. Im BW/4HANA besteht dieses Problem nicht mehr, siehe SAP Dokumentation. In diesem Artikel will ich ein paar Lösungsansätze aufzeigen. Beispiel für semantische Gruppierung Es soll berechnet werden, wie groß der prozentuale Anteil einer Position an einem Beleg ist. Das lässt sich nur berechnen, wenn alle Daten eines Belegs im gleichen Paket sind. Alles in einem Paket ➔ Richtiges Ergebnis Beleg Position Betrag 4711 10 100 4711 20 300 4711 30 500 4711 40 100 ➔ Beleg Position Betrag Anteil 4711 10 100 10% 4711 20 300 30% 4711 30 500 50% 4711 40 100 10% Verteilt auf zwei Pakete ➔ Falsches Ergebnis Paket 1 Beleg Position Betrag 4711 10 100 4711 20 300 Paket 2 Beleg Position Betrag 4711 30 500 4711 40 100 ➔ Paket 1 Beleg Position Betrag Anteil 4711 10 100 25% 4711 20 300 75% Paket 2 Beleg Position Betrag Anteil 4711 30 500 83,33% 4711 40 100 16,66% Lösungsansätze Es gibt mehrere Möglichkeiten, um die Semantische Gruppierung bei HANA Ausführung in BW 7.50 zu ersetzen. Was der beste Weg ist, hängt natürlich wie immer von den Gegebenheiten ab. Die Lösung im SQLScript Dieser Ansatz löst das Problem im AMDP. Wir lesen zu den Daten in der INTAB alle Datensätze aus der aktiven Tabelle des Quell-DSO, entsprechend der semantischen Gruppierung. Damit erstellen wir eine neue Tabellenvariable INTAB_NEW, die in sich konsistent ist. Wenn jetzt eine semantische Gruppe auf zwei Pakete verteilt ist, dann werden alle Datensätze der Gruppe doppelt verarbeitet. Wir gehen aber davon aus, dass es sich beim Ziel um ein Standard-DSO handelt. Das bedeutet, das wenn ein Datensatz mit gleichem Schlüssel in zwei oder mehreren Paketen ist, dann wird er sich beim Aktivieren mit dem jeweils exakt gleichen Werten überschrieben. Hier der Quellcode, mit dem wir die INTAB_NEW aufbauen: METHOD GLOBAL_EXPERT BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY using /BIC/AZBR_E2_S2. intab_new = select * from "/BIC/AZBR_E2_S2" where ( budat, account ) in ( select distinct budat, account from :intab ); Nachteil der Semantischen Gruppierung in SQLScript Mit der gezeigten Logik erreichen wir, dass immer alle Datensätze zusammen bleiben. Aber damit werden auch Daten doppelt verarbeitet. Das stört im Ergebnis inhaltlich nicht, da die Logik in allen Paketen stets zum gleichen Ergebnis kommt. Aber es werden mehr Datensätze erzeugt, als tatsächlich benötigt werden. Das kann die Laufzeit etwas beeinflussen. In meinen Tests waren das bei dem Beispiel für ca. 3.1 Mio Datensätzen und 4 Paketen eine Überlappung unter 5%. Das ist im Vergleich mit den anderen Optionen auf jeden Fall akzeptabel. Ich weiss aber nicht, ob man den Wert verallgemeinern kann. Wie hoch die Redundanz tatsächlich ist, merkt man bei der Aktivierung an der Differenz der Anzahl der Datensätze zwischen Eingangstabelle und aktiver Tabelle. Vermutlich hilft eine feine Granularität der Gruppen dabei, die Redundanz gering zu halten. Ein großes Paket Wenn man die Paketgröße so wählt, dass niemals zwei Pakete gebildet werden, dann hat man das Problem nicht. Allerdings kann auch eine HANA Datenbank nicht mit unendlich vielen Daten gleichzeitig umgehen. Darum ist das nur eine Option, wenn man absehen kann, dass das Volumen überschaubar bleibt. Ich gehe davon aus, dass wenige Millionen Datensätze akzeptabel sind. Und falls die Datenmenge unvorhergesehen zunimmt? Der Parameter für die Paketgröße muss so gesetzt werden, dass dieser wirklich niemals überschritten wird. Sonst bekommen wir falsche Daten. Wenn es hingegen Abbrüche gibt, dann ist das zwar ärgerlich, aber zur Not bekommt man die Daten dann mit dem nächsten Lösungsansatz ins System. Mehrere DTPs mit disjunkter Datenmenge Wenn man zu viele Daten für ein Paket hat, gerade wenn man mit dem vorherigen Ansatz Probleme bekommen hat, dann ist es eine Option, dass man manuell partitioniert. Damit meine ich, dass man mehrere DTPs anlegt, die anhand der Filterkriterien die Datenmenge aufteilen. Das ist nicht schön, da es die Wartbarkeit des Systems reduziert. Ist aber immer möglich. Upgrade auf BW/4HANA Nur der Vollständigkeit halber. Wer viel semantische Gruppierung nutzt, für den könnte das ein Argument für BW/4HANA sein. Auf ABAP Ausführung umstellen Die letzte Option ist die Umstellung auf ABAP. Das ist gerade für kleinen Datenmengen bzw. wenn die Ausführungszeit kein Problem darstellt, eine valide Option. Nicht schön, geht aber garantiert. Fazit Es ist schade, dass die semantische Gruppierung für die HANA Ausführung erst mit BW/4HANA sauber funktioniert. Aber es gibt einige praktikable Workarounds. Im SQLScript ist das wenig kompliziert. Ich hoffe, der Artikel ist nützlich. Über Anmerkungen und Erfahrungen damit freue ich mich natürlich sehr.