Nicht erlaubte Zeichen im SAP BW/4HANA mit SQLScript behandeln
Veröffentlicht am 6. November 2020 von | ABAP | AMDP | BW/4HANA | SQLScript |
Mit SQLScript lassen sich Fehler wegen der nicht erlaubten Zeichen im SAP BW elegant und performant beim Laden vermeiden. Dieser Artikel gibt eine Übersicht über die Problemstellung und zeigt die Lösungsansätze in AMDP Transformationsroutinen auf, um die Daten zu bereinigen.
Das Problem
Die erlaubten Zeichen
Im SAP BW sind nicht alle Zeichen in Merkmalsausprägungen erlaubt. Im Standard sind das nur die folgenden Zeichen:
`!"%&'()*+,-./:;<=>?_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`
Wer mehr als diese benötigt, was bei einer internationalen Kundschaft quasi jeder SAP Kunde sein dürfte, muss das im BW Customizing Leitfaden oder in der Transaktion RSKC
separat einstellen. Hierzu gibt es drei Optionen, die nicht kombiniert werden können:
Einzelne Zeichen
Entweder erfasst man hier eine lächerlich kurze Liste von 75 weiteren Zeichen. Diese wird früher oder später nicht ausreichen, da weder Umlaute, Akzente oder asiatische Schriftzeichen im Standard erlaubt sind.
ALL_CAPITAL
Oder man nimmt mit der Angabe von ALL_CAPITAL
pauschal alle Zeichen mit dazu (SAP Hinweis 173241 - erlaubte Zeichen im BW) .
ALL_CAPITAL _PLUS_HEX
Von der öfters beschriebenen Verwendung von ALL_CAPITAL_PLUS_HEX
im Customizing wird von der SAP explizit abgeraten, da hier auch Steuerzeichen in den Merkmalsausprägungen erlaubt sind. Das kann bei den Frontend-Tools zu Problemen führen (SAP Hinweis 1075403 - nicht erlaubte Zeichen und ALL_CAPITAL). Diese Option ist nur für die kurzfristige Aufrechterhaltung des Produktivbetriebs gedacht. Falls sie doch mal zum Einsatz kommen muss, dann sollten die Daten zeitnah bereinigt werden und danach der Wert zurückgestellt werden.
Unabhängig von den obigen Einstellungen darf ein Merkmalswert niemals nur aus einem einfachen Doppelkreuz bzw. Hash '#' bestehen und nicht mit einem Anführungszeichen '!' beginnen.
Behandlung der nicht erlaubten Zeichen in ABAP
In ABAP Transformationsroutinen gibt es unterschiedliche Ansätze zum Entfernen der unerlaubten Zeichen. Im Blog von Denis Reis finden sich beispielsweise Ideen, wie man das "von Hand" machen kann, solange man eine Positiv-Liste der erlaubten Zeichen hat (Denis Reis: Nützliche ABAP Transformationsroutinen).
Funktionsbausteine
Und natürlich gibt es mehr als einen Funktionsbaustein um das zu erledigen. Mit dem Fuba RSKC_CHAVL_CHECK
kann man die Werte gegen die aktuellen Einstellungen des Systems prüfen. Und mit dem Fuba SCP_REPLACE_STRANGE_CHARS
(SAP Hinweis 1062237 - Transliteration von Zeichen mit SCP_REPLACE_STRANGE_CHARS) können verdächtige Zeichen ersetzt werden. Das es bei diesem Fuba zu Performanceproblemen kommen kann ist bekannt (SAP Hinweis 2305674 - Performance of SCP_REPLACE_STRANGE_CHARS is bad).
Analyse von Abbrüchen wegen nicht erlaubter Zeichen in SQLScript
Wenn das Laden bzw. Aktivieren von Daten wegen unerlaubten Zeichen abbricht, dann geht es zunächst darum zu verstehen, warum der Abbruch passiert. Das konnte man im ABAP Debugger immer gut erkennen, da hier auch der Hex-Code jedes Zeichens sichtbar ist.
Mit der skalaren Funktion UNICODE
kann man in SQLScript den entsprechenden Unicode- Nummer eines Zeichens ermitteln (SAP Dokumentation UNICODE).
Jetzt müssen wir also nur noch jeden einzelnen Buchstaben eines fehlerhaften Wertes analysieren. Dazu erstellen wir in der SQL-Konsole einen anonymen Block, in dem wir die verdächtige Zeichenketten mit Hilfe der SERIES_GENERATE_INTEGER
Funktion zerlegen. Das Beispiel geht auf eine Tabelle aus dem Demo-Datenmodell aus meinem Buch und muss angepasst werden. Die entsprechenden Stellen sind mit Kommentaren versehen.
DO BEGIN
lt_suspect_data = SELECT title AS suspect --Replace with the column name
FROM tasks --Replace by your table e.g. "/BIC/A...2"
WHERE id = 1; --Restrict to the corrupt data
SELECT suspect,
element_number,
SUBSTR(suspect, element_number, 1),
UNICODE(SUBSTR(suspect, element_number, 1))
FROM :lt_suspect_data
CROSS JOIN SERIES_GENERATE_INTEGER( 1,1,60)
WHERE SUBSTR(suspect, element_number, 1) <> ''
ORDER BY suspect,
element_number;
END;
Auch wenn der eingangs genannte Hinweis vor allem die Steuerzeichen als Fehlerquelle nennt, so kann es doch auch anderen Zeichen geben, die Probleme verursachen. Auf diese Art und Weise haben wir in einem Projekt beispielsweise das Unicodezeichen 0160
identifiziert, das sich partout nicht laden lassen wollte. Es handelt sich um einen sogenannten non-breaking space (NBSP), der auch in anderem Kontext für Probleme sorgt (SAP Hinweise hierzu).
Non-Breaking Space
Behandeln von nicht erlaubten Zeichen in SQLScript
Einzelne Zeichen
Wenn es um das Ersetzen von einzelnen Zeichen geht, dann ist die einfachste Methode die SQL-Funktion REPLACE. Eine Schwierigkeit beim Programmieren ist, das das zu ersetzende Zeichen nicht auf der Tastatur befindet. Wenn wir aber seine Unicode Zeichennummer kennen, dann können wir mit der SQL-Funktion NCHAR(Zeichennummer)
das Zeichen erzeugen.
OutTab = SELECT ...
REPLACE("/BIC/BUTXT", NCHAR(0160), ' ') AS "/BIC/BUTXT",
...
FROM :InTab;
Wenn auf diese Weise mehrere Zeichen ersetzt werden sollen, muss man die REPLACE
Funktion schachteln. Das reduziert die Übersichtlichkeit und damit die Wartbarkeit.
Ersetzen mit regulären Ausdrücken
Reguläre Ausdrücke beschreiben ein Muster, mit dem man auch Ersetzungen vornehmen kann. In SQLScript gibt es hierzu die SQLFunktion REPLACE_REGEXPR. In diesen Mustern können mit der folgenden Notation auch UNICODE-Zeichen (siehe auch hier) eingegeben werden:
x{<HEX-Zeichennummer>}
also beispielsweise x{A0}
für den oben genannten Non-Breaking Space (NBSP). Und mit der eckigen Klammer lässt sich auch relativ elegant nach mehreren Unicode Zeichen gleichzeitig suchen.
OutTab = SELECT ...
REPLACE_REGEXPR('[x{A0}\x{8239}]' IN title WITH ' ') AS "/BIC/BUTXT",
...
FROM :InTab;
Allerdings müssen wir hierzu noch alle Zeichen einzeln im HEX-Zeichennummer benennen.
Alle Steuerzeichen
Wenn alle im SAP Hinweis 1075403 genannten Steuerzeichen HEX00 bis HEX1F entfernt werden sollen, so kann man das ebenfalls sehr elegant mit einem regulären Ausdruck machen. Die Zeichenklasse[[:cntrl:]]
umfasst eben genau diese Zeichen (Wikipedia Reguläre Ausdrücke).
Die entsprechende Abfrage sieht dann so aus:
OutTab = SELECT ...
REPLACE_REGEXPR('[[:cntrl:]]' IN title WITH '') AS "/BIC/BUTXT",
...
FROM :InTab;
Die Zeichenklasse [[:cntrl:]]
lässt sich auch mit einzelnen Zeichen wie dem NBSP kombinieren, was den Ausdruck ergibt, den ich persönlich in aktuellen Projekten verwenden würde:
OutTab = SELECT ...
REPLACE_REGEXPR('[[:cntrl:]\x{00A0}]' IN title WITH '') AS "/BIC/BUTXT",
...
FROM :InTab;
Warnung - bitte Testen
Leider sind reguläre Ausdrücke extrem fragil. Eine Kleinigkeit kann den Ausdruck komplett unbrauchbar machen. Das bedeutet, das wir unsere Logik immer zuvor testen sollten. Das mache ich normalerweise in der SQL-Konsole mit einem kleinen Beispiel:
create table test_char(line nvarchar(100));
insert into test_char values ('ABCDE');
insert into test_char values ('AB'||nchar(160)||'CDE');
insert into test_char values (nchar(0010)||'ABCDE');
insert into test_char values ('AB'||nchar(10)||'CDE');
insert into test_char values ('ABCDE');
SELECT line,
REPLACE_REGEXPR('[[:cntrl:]\x{00A0}]' IN line WITH '#') AS "/BIC/BUTXT"
from test_char;
Fazit
Es gibt auch in SQLScript sehr elegante Möglichkeiten, die nicht erlaubten Zeichen zu entfernen. Fundierte SQLScript Kenntnisse und sorgfältiges Testen sind aber hier sehr wichtig.
Ich freue mich, wenn Ihnen dieser Text bei Ihrer Arbeit in den Projekten hilfreich ist. Über Verlinkung, Zitate mit Quellenangaben und vor allem Erwähnungen in sozialen Medien würde ich mich freuen. Und natürlich auch über jede Form von Feedback, falls Sie andere oder ergänzende Erfahrungen gemacht haben.