Sicherlich ist es heute unüblich, eine Oracle Datenbank mit dem Character Set US7ASCII anzulegen. Über den dbca (database configuration assistant) in Oracle 11g bekommt man standardmäßig noch nicht einmal US7ASCII angezeigt. Möglich ist es allerdings trotzdem – auch in der aktuellen Version 11.2.0.3! Ob das sinnvoll ist, steht allerdings auf einem anderen Blatt.
Migration von US7ASCII nach Unicode
In einem meiner derzeitigen Projekte wurde ich gefragt, ob ich eine Migration einer Oracle 10g Datenbank nach Unicode durchführen kann, weil in der Datenbank auch kyrillische und arabische Zeichen abgespeichert werden sollen. Ausgehend von der Annahme, dass es sich um eine übliche Datenbank mit einem WE8-Zeichensatz handelt, habe ich ein Checkskript ausführen lassen, was ich gerne verwende, um mir einen Überblick über die Datenbank zu verschaffen.
Und welch Wunder: Die Datenbank arbeitet mit dem Character Set US7ASCII! Da gab es doch den Befehl ALTER DATABASE CHARACTER SET…„. Da ja US7ASCII ein vollständiges Subset aller anderen Character Sets, also auch von AL32UTF8 ist, sollte das ja funktionieren. Aber diesen Befehl gibt es seit Oracle 9i nicht mehr, weil Oracle im Data Dictionary den Datentyp LOB verwendet. In neueren Versionen wird dieses Kommando durch den Character Set Scanner ersetzt.
Der Character Set Scanner besteht aus drei Teilen: zunächst einmal wird mit dem Skript csminst.sql (im Verzeichnis $ORACLE_HOME/rdbms/admin) das Schemas csmig mit einigen Tabellen angelegt. Anschließend wird mit dem Befehl csscan, der als Parameter unter Anderem den Zielzeichensatz bekommt, die Datenbank analysiert. Nicht konvertierbare Objekte oder Datensätze werden protokolliert und in entsprechende Tabellen im Schema csmig eingetragen. Nur wenn es keine Fehler gibt, kann im dritten Schritt mit der Prozedur csalter.plb (im Verzeichnis $ORACLE_HOME/rdbms/admin) die Datenbank auf den neuen Zeichensatz umgestellt werden. In unserem Fall lieferte der Befehl csscan aber hunderttausende von fehlerhaften Datensätze, weil die Anwendungen mit Umlauten oder Sonderzeichen gearbeitet hatten.
Wie kommen die fehlerhaften Sätze in die Datenbank?
Für die Konvertierung von Zeichen zwischen einer Anwendung und der Oracle Datenbank ist das Oracle Two-Task-Protokoll, d.h. die Schicht zwischen Anwendungs- und Serverprozess verantwortlich. Wenn z.B. eine Anwendung auf einem Microsoft Rechner betrieben wird, dann bedeutet dies, dass der Client (Microsoft) in der Regel den Zeichensatz WE8MSWIN1252 verwendet. Wurde die Datenbank mit einem Unicode-Zeichensatz (z.B. AL32UTF8) erstellt, so wird Oracle automatisch ein „€“, welches auf dem Client durch den Code 0x80 repräsentiert wird in ein 0x20AC (also einen Zwei Byte Code) konvertieren und umgekehrt. Wenn die Datenbank, wie im vorliegenden Fall, aber mit US7ASCII angelegt wurde, wird die Datenbank das Zeichen ablehnen und stattdessen ein „?“ als ungültiges Symbol abspeichern (egal ob € oder ein Umlaut). In der Regel wird der Zeichensatz durch das Betriebssystem oder die Anwendung vorgegeben. Ein gutes Beispiel hierfür ist das Programm „putty“, bei dem der Client-Zeichensatz explizit gesetzt werden kann.
Allerdings findet diese Konvertierung nur dann statt, wenn Client und Datenbank unterschiedliche Zeichensätze benutzen (was in der Regel der Fall ist). Über den Parameter NLS_LANG kann der Zeichensatz, den die Anwendung bzw. das Betriebssystem benutzt „übersteuert“ werden. Der Parameter besteht aus drei Komponenten:
- Sprache (z.B. für Fehlermeldung, etc.)
- Territorium bzw. Land (z.B. für Sortierungen, Währungssymbol oder ., Notation bei Zahlen)
- Zeichensatz
Mit dem Befehl NLS_LANG=GERMAN_GERMANY.WE8ISO8859P15 wird, egal wie der eigentliche Zeichensatz des Betriebssystem ist, der Client mit dem ISO-Zeichensatz arbeiten. Falls die Oracle Datenbank den gleichen Zeichensatz verwendet, wird keine weitere Konvertierung stattfinden.
Im Falle der analysierten Datenbank bedeutet dies, dass für bestimmte Anwendungen bzw. Betriebssysteme der Zeichensatz über die Variable NLS_LANG explizit auf US7ASCII eingestellt worden ist und damit die Zeichensatzkonvertierung ausgeschaltet wurde.
Das bedeutet aber auch, dass nur der identische Client in der Lage ist, die Zeichen so wieder auszulesen. Ein Client, der einen anderen Zeichensatz verwendet (z.B. Quest Toad for ORACLE oder SQL Developer) wird die Zeichen nicht richtig darstellen können, sondern stattdessen z.B. ein „?“ anzeigen.
In diesem Projekt war es sogar so, dass unterschiedliche Clients verwendet wurden, also z.B. Windows Rechner mit dem o.g. WE8MSWIN1252 Zeichensatz, Unix Rechner mit WE8ISO8859P15 und Java Anwendungen mit UFT8 Zeichensatz.
Wie kann man diese Datenbank migrieren?
Prinzipiell gibt es die Möglichkeit, beim Befehl csscan nicht den tatsächlichen Zeichensatz der Datenbank zu verwenden sondern einen x-beliebigen. Somit ist es möglich über den Befehl:
csscan system/<pwd> FULL=Y FROMCHAR=WE8ISO8859P15 TOCHAR=WE8ISO8859P15
einen Scan durchzuführen, als würde die Datenbank bereits mit dem Zeichensatz WE8ISO8859P15 arbeiten. Allerdings führte auch dieser Scan zu etlichen Fehlern, weil, wie bereits beschrieben, mit unterschiedlichen Clients gearbeitet worden war. Wenn einem diese Fehler egal sind (weil es nur wenige Datensätze betrifft, die man später manuell nacharbeiten kann), dann können die im Schema csmig die Einträge in den Tabellen (hauptsächlich csm$columns) einfach gelöscht werden. Danach kann dann das Package csalter.plb ausgeführt werden. Allerdings ist dies eine Einbahnstraße, d.h. nach der Konvertierung hat die Datenbank den Zeichensatz WE8ISO8859P15 und kann nicht für einen zweiten Scan verwendet werden.
ALTER DATABASE CHARACTER SET …
Eingangs habe ich erwähnt, dass der Befehl ALTER DATABASE CHARACTER SET seit der Version 10g nicht mehr funktioniert. Das stimmt so nicht ganz, es gibt eine Option, mit der dieser Befehl immer noch genutzt werden kann:
SQL> ALTER DATABASE CHARACTER SET INTERNAL_USE ;
Damit wird einfach der Data Dictionary Eintrag für den Zeichensatz der Datenbank geändert und keine weiteren Anpassungen vorgenommen. Das ist natürlich für eine produktive Datenbank sehr kritisch, weil dadurch die gesamte Zeichenkonvertierung nicht mehr funktioniert. Da wir aber nur einen Export der Daten machen wollen, ist er hier genau richtig. Das Vorgehen für die Konvertierung aus unterschiedlichen Client Zeichensätzen ist also:
SQL> ALTER DATABASE CHARACTER SET INTERNAL_USE WE8ISO8859P15;
anschließender Export aller Schemata, die mit einer Anwendung mit diesem Zeichensatz gearbeitet haben.
SQL> ALTER DATABASE CHARACTER SET INTERNAL_USE WE8MSWIN1252;
wiederum ein Export der Schemata mit diesem Zeichensatz
und zuletzt:
SQL> ALTER DATABASE CHARACTER SET INTERNAL_USE UTF8;
und wieder den Export der entsprechenden Schemata.
Damit kann jetzt die neue Unicode Datenbank aufgebaut werden und wir hoffen, dass wir keine kryptischen Zeichen mehr haben – außer denen, die durch kyrillisch oder arabisch jetzt neu dazu kommen.