Site Logo

IzzySoft


Das inoffizielle Android-HandbuchDas inoffizielle Android-Handbuch
Für EUR 16,99 bei Amazon kaufen
Das inoffizielle Android-SystemhandbuchDas inoffizielle Android-Systemhandbuch
Für EUR 7,00 bei Amazon kaufen
Die besten Android-Apps: Android-Systemtools. Fotografie & Freizeit. Büro-Tools, Schule und StudiumDie besten Android-Apps: Android-Systemtools. Fotografie & Freizeit. Büro-Tools, Schule und Studium
 
Stand: 2018-12-19 06:46
Preis & Verfügbarkeit können sich geändert haben.

Datenbank-Migration

Dieser Artikel beschreibt die für die Migration einer Oracle Datenbank zum UTF8 Zeichensatz nötigen Schritte. Er geht dabei von einer Oracle 9i Datenbank aus, die den Zeichensatz WE8ISO8859P15 (Latin 15) verwendet – sollte aber prinzipiell auch auf andere Konfigurationen zutreffen.

Inhalt

Datenbereinigung

Bevor wir uns der eigentlichen Migration zuwenden können, sollten bzw. müssen wir in unserer bestehenden Datenbank zunächst ein wenig aufräumen. Dafür gibt es mehrere gute Gründe:

Dies nennt nur einige Punkte – zeigt aber auch, dass eine Datenbereinigung nicht nur für den Migrationsprozess hilfreich ist, sondern auch darüber hinaus einige positive Aspekte für die spätere tägliche Arbeit und Wartung mitbringt. Sollte es daher in der Datenbank Daten geben, die nicht mehr benutzt werden können oder sollen: Ein guter Zeitpunkt, damit aufzuräumen. Auch wenn es dann letztendlich vielleicht doch nicht zu einer Migration kommen sollte …

Löschkandidaten identifizieren

In jeder Datenbank finden sich nach einer gewissen Laufzeit Tabellen, die nicht länger genutzt werden - und daher nach einem optionalen Export (Backup für den Fall des Falles) gelöscht werden können. Diese lassen sich grob in drei Kategorien einteilen:

Darüber hinaus wären da noch komplette Schemata (z. B. von ehemaligen Anwendungen oder Mitarbeitern), die nicht mehr genutzt werden – und sich ebenfalls in obige Kategorien einordnen ließen. Außerdem wären da noch "historische" Datensätze, auf die nicht mehr zugegriffen wird – und die daher ebenso behandelt werden können.

Einmal sicher identifiziert, sollten diese Objekte zügig gesichert und anschließend gelöscht werden – bevor sie erneut in Vergessenheit geraten.

Daten Integrität

Eine Verifikation der Logik der Datenbankschemen fördert häufig etliche fehlende Fremdschlüssel ("Foreign Key Constraints") zutage, deren Verwaltung man einfach der jeweiligen Applikation überließ. Schaut man sich nun an, wie gut selbige dies gehandhabt hat, findet man nicht selten einige "Witwen und Weisen" ("Orphans" – Daten ohne Bezug, die für sich genommen, also ohne ihren nicht mehr vorhandenen Kontext, keinen Sinn mehr ergeben). Auch wenn es in dieser Formulierung "unethisch" klingen mag, sollte der DBA hier zum "Witwen-und-Waisen-Mörder" werden – diese Daten braucht wahrlich keiner mehr.

Zum Auffinden dieser "Orphans" ist natürlich eine gute Kenntnis des Datenmodells erforderlich. Weitere Indikatoren sind Constraints, die nicht aktiviert bzw. zwar aktiviert, aber nicht verifiziert sind. Hier gilt es zu entscheiden, ob diese Constraints dem Datenmodell zufolge sinnvoll sind – wenn ja, sollten sie aktiviert und verifiziert, andernfalls entfernt werden. Wobei das Verifizieren ("validate") wahrscheinlich erneut einige "Witwen und Waisen" zutage fördern wird, die es sodann ebefalls zu löschen gilt.

Um in Zukunft derartige "Orphans" zu vermeiden, sollten die fehlenden Fremdschlüssel nun zügig angelegt werden – natürlich nachdem mögliche Nebeneffekte in den jeweiligen Applikationen (die z.B. beim "Selbstversuch" auf Constraint-Verletzungen stoßen könnten) ausgeschlossen wurden. Etwa notwendige Anpassungen in diesen Applikationen sollten dann zeitgleich mit den neuen Constraints aktiviert werden.

Problematische Objekte

Zusätzlich zu oben bereits diskutierten Objekten, gibt es noch weitere Dinge, die in Bezug auf die Datenintegrität geprüft werden sollten:

Folgendes SQL Skript sucht diese möglichen Unruhestifter aus der Datenbank, und listet sie auf. Die gefundenen Objekte sollten sorgfältig inspiziert werden, da ihre Bedeutung von "völlig unwichtig" bis "kritisch" reichen kann: Einige Trigger sind evtl. mit gutem Grund deaktiviert (und sollten keineswegs aktiviert, sondern besser gelöscht werden – damit sie nicht versehentlich nach der Migration aktiviert werden) - während einige Statistiken vielleicht nur "veraltet" sind, weil die zugrunde liegenden Daten statisch sind (z.B. Postleitzahlen oder ähnliche "Lookup-Tabellen").

--
-- Discover possible problematic objects
--
-- Author: AI Rehberg
-- $LastChangedDate: 2007-08-31 15:04:37 +0200 (Fr, 31 Aug 2007) $

-- disabled triggers
select *
  from dba_triggers
 where status != 'ENABLED'
   and owner not in 'SYS'
 order by owner

-- Invalid objects (packages,views,...)
select owner,object_name "Object",object_type "OType",status,temporary "Temp"
  from dba_objects
 where status != 'VALID'
   and owner not in ('SYS')
 order by owner

-- Possible broken packages/procedures/views/...
-- (may give detail information on above selected broken objects
select distinct owner,name,type,line,position,text
  from dba_errors
 where text not like '%Statement ignored%'
 order by owner,name

-- Disabled constraints
select owner,constraint_name,constraint_type "CType",table_name,status,validated,last_change
  from dba_constraints
 where 1=1
   and status !='ENABLED'
   and owner not in ('SYS','SYSTEM','WMSYS')
 order by owner

-- Not validated constraints
select owner,constraint_name,constraint_type "CType",table_name,status,validated,last_change
  from dba_constraints
 where 1=1
   and validated!='VALIDATED'
   and owner not in ('SYS','SYSTEM','WMSYS')
 order by owner

-- Indices with outdated or no statistics
select owner,index_name,index_type "IType",table_name,uniqueness,tablespace_name,logging "Log",status,last_analyzed,temporary "Temp"
  from dba_indexes
 where 1=1
   and owner not in ('SYS','SYSTEM','WMSYS','CSMIG','CTXSYS','MDSYS','ORDSYS','OUTLN','TOAD','XDB')
   and (last_analyzed is null or last_analyzed < add_months(sysdate,-2))
 order by owner

-- Invalid indices
select owner,index_name,index_type "IType",table_name,uniqueness,tablespace_name,logging "Log",status,last_analyzed,temporary "Temp"
  from dba_indexes
 where 1=1
   and owner not in ('SYS','SYSTEM','WMSYS','CSMIG','CTXSYS','MDSYS','ORDSYS','OUTLN','TOAD','XDB')
   and status != 'VALID'
 order by owner

Zeichensatz-Bereinigung

Dieses Thema betrifft hauptsächlich zwei Probleme, und resultiert in einem Double-Check der Datenbank sowie ggf. Änderungen bzgl. der Umgebungsvariablen:

  1. Falsche Zeichen in der Datenbank
  2. Zu konvertierende Zeichen in der Datenbank

Das erste Problem, so es denn besteht, wurde mit großer Wahrscheinlichkeit von falsch (oder gar nicht) gesetzten Umgebungsvariablen verursacht. Wichtigster Kandidat ist hier die (oft missverstandene) Variable NLS_LANG, die den Zeichensatz der Client-Applikation (und nicht den der Datenbank) beinhalten muss. Oftmals wurde hier stattdessen – unabhängig von den vom Client selbst verwendeten Tatsachen – einfach der Datenbank-Zeichensatz (in unserem Beispiel also 'WE8ISO8859P15') eingestellt. Verwendet der Client nun tatsächlich einen anderen Zeichensatz (bei Windows üblicherweise 'WE8MSWIN1252' – auch als Windows-1252 bekannt), kommt es zu eben diesem Problem: Der Oracle Server geht davon aus, dass die ihm übermittelten Zeichen in dem durch NLS_LANG definierten Zeichensatz (hier: Latin-15) sind, und speichert sie als solche ab. Eine spätere Konvertierung geht also von falschen Tatsachen aus – sodass die meisten Sonderzeichen zerstört werden. Dies würde also auch in jedem Fall passieren, wenn wir unsere Datenbank konvertieren, ohne dieses Problem zuvor gelöst zu haben – statt der Sonderzeichen sehen wir dann nur noch Fragezeichen.

Das zweite Problem ist einfach nur das, um welches wir uns ja gerade kümmern wollen: Da wir unsere Datenbank auf den Zeichensatz 'AL32UTF8' (UTF8) umstellen wollen, müssen einige Zeichen konvertiert werden. Glücklicherweise ist UTF8 ein sogenanntes "Super-Set" unseres Ausgangszeichensatzes Latin-15 (d.h., alle Zeichen von Latin-15 gibt es auch in UTF8), so dass diese Konvertierung kein großes Problem darstellen sollte.

Korrektur falscher Zeichen in der Datenbank

Wie oben bereits geschildert: Ohne Korrektur würden diese falschen Zeichen einfach durch Platzhalter ersetzt (i. d. R. ein Fragezeichen, oder ein auf dem Kopf stehendes Fragezeichen) – eine korrekte automatische Umwandlung ist nicht möglich. Unsere Korrektur erfolgt nun in drei Schritten:

  1. Identifizierung der betroffenen Daten
  2. Ersetzen der falschen Zeichen
  3. Berichtigen der Umgebungsvariablen (d. h. z. B., unter Windows NLS_LANG=WE8MSWIN1252 verwenden)

Bei unserem ersten Schritt unterstützt uns Oracles Character Set Scanner (CSScan). Um unsere Problemfälle zu identifizieren, geben wir einfach vor, wir würden von unserem derzeitigen Zeichensatz in eben denselben konvertieren wollen (wir verwenden also WE8ISO8859P15 sowohl für den "from" als auch den "to" Zeichensatz). CSScan wird nun alle Datensätze auflisten, deren Zeichen nicht alle in Latin-1 gehalten sind (in userem Beispiel also alle Spezialzeichen aus dem Windows-1252 Zeichensatz). Alle Zeilen, die bei einem derartigen Lauf als "not convertible" (nicht konvertierbar) aufgeführt werden, müssen nun zunächst manuell korrigiert werden – und zwar bevor wir mit dem nächsten Schritt fortfahren.

Zu konvertierende Zeichen

Nachdem alle "falschen Zeichen" korrigiert sind (siehe oben) – d. h. unsere Datenbank ausschließlich Zeichen enthält, die ihrem verwendeten Zeichensatz entsprechen – und nicht vorher, können wir auf die Folgen unserer Konvertierung prüfen. Der Prozess läuft ähnlich wie der vorige – nur geben wir dieses Mal den Zeichensatz als Ziel an, zu dem wir auch tatsächlich konfertieren wollen: AL32UTF8. Das Ergebnis dieses Character Set Scanner Laufes teilt unsere Daten in 4 Kategorien auf:

Mit etwas Glück gehören die meisten unserer Daten in die Kategorien 1 und 2, sowie möglichst wenige (am besten gar keine) in Kategorie 3. Kandidaten für Kategorie 4 sollten wir eigentlich bereits beseitigt haben – falls nicht, zurück zum entsprechenden Abschnitt und richtig aufräumen.

Die Kandidaten der dritten Kategorie benötigen ein wenig extra Aufmerksamkeit. Hier müssen wir unsere Tabellen-Definitionen prüfen und ggf. nachbessern, d. h. die entsprechenden Spalten in der Größe derart anpassen, dass unsere Daten wieder hineinpassen. Sollte dies nicht möglich sein, passen wir die entsprechenden Datensätze besser von Hand an – damit der Inhalt verständlich bleibt. Das ist jedoch nur bei VARCHAR2 Feldern nötig, deren Inhalt in Folge der Konvertierung mehr als 4.000 Bytes umfasst (einige Sonderzeichen benötigen in UTF8 mehr als ein Byte). Keine Sorge also bei Datensätzen, deren Felder z. B. vom Typ VARCHAR(50) sind: CSScan sieht hier nur die aktuelle Spaltendefinition (die, ausgeschrieben, VARCHAR(50 BYTE) lautet). Wir ändern später die Semantik derart, dass die Spalte vom Typ VARCHAR2(50 CHAR) ist. Das ändert allerdings nichts daran, dass VARCHAR2 generell auf maximal 4.000 Byte limitiert ist – doch diese sprengen wir nicht mit einem VARCHAR2(50). "Gefährlich" wird es hier erst ab ca. VARCHAR2(2000), wenn der Inhalt nur aus Sonderzeichen besteht …

Vorbereitung der Datenbank-Strukturen

In der Regel müssen wir für die Migration eine neue Datenbank aufsetzen, die von Anfang an gleich mit dem Zeichensatz AL32UTF8 angelegt wird. Die Fälle, in denen ein einfaches ALTER DATABASE CHARACTER SET AL32UTF8 genügt, sind ziemlich rar. Es gilt also folgendes Rezept:

Wie die gerade genannten 4 Schritte umzusetzen sind, sollte jeder DBA wissen; daher gehe ich da jetzt nicht ins Detail – sondern konzentriere mich auf das Wesentliche ;)

NLS Length Semantics

Wenn nicht explizit anders vorgegeben, ist der Parameter NLS_LENGTH_SEMANTICS auf BYTE eingestellt. Im Klartext heißt dies: Wird eine Spalte als VARCHAR2(50) definiert, übersetzt Oracle dies als VARCHAR2(50 BYTES). Aber was hat dies für Folgen? Für unsere Latin-15 Datenbank eigentlich gar keine: Da jedes Zeichen hier genau ein Byte belegt, sind die Semantiken BYTE und CHAR hier gleichbedeutend. Bei UTF8 sieht das jedoch anders aus, da hier manche Zeichen mehr als nur 1 Byte belegen. Ist die Semantik also hier auf BYTE gesetzt, lässt sich nicht sagen, wie viele Zeichen hier "per se" in ein VARCHAR2(50) passen. Oder ob all unsere VARCHAR2(50) Felder sich problemlos konvertieren lassen.

Um dieses Problem (weitgehend) zu vermeiden, setzen wir explizit NLS_LENGTH_SEMANTICS=CHAR. Somit interpretiert Oracle unsere obige Definition auch als VARCHAR2(50 CHAR) – und wir wissen, dass in jedem Fall genau 50 Zeichen hier hinein passen – auch wenn sie mehr Bytes benötigen. Die Beschränkung auf 4.000 Zeichen lässt sich damit natürlich nicht umgehen – wie viele Zeichen also in ein VARCHAR2(4000 CHAR) Feld passen, hängt von den verwendeten Zeichen ab.

Semantik Konvertierungen

Jetzt haben wir also die NLS_LENGTH_SEMANTICS explizit auf BYTE gesetzt, unsere UTF8 Datenbank angelegt, und die Strukturen importiert. Man könnte also denken, dass wir jetzt einfach die Daten nachziehen können. Leider falsch: Oracle hat die Strukturen so importiert, wie sie ursprünglich angelegt wurden – also auch mit der derzeitigen Semantik. Was ja durchaus seine Berechtigung hat …

… uns aber mit einer weiteren Aufgabe konfrontiert: Wir müssen nun für all unsere Tabellen die Semantiken anpassen. Zum Glück haben wir dafür ein passendes Skript zur Hand, um nicht alles manuell machen zu müssen. Nein, das Skript wird nicht von Oracle bereitgestellt – Zeit also, endlich mein IZ_MIGRATION Package vorzustellen, was u. a. die hierfür benötigte Prozedur bereitstellt. Das Package steht unter der GPL, kann also kostenlos bezogen werden. Näheres dazu findet sich hier auf IzzySoft.DE.

Und die genannte Prozedur ist iz_migration.semantics2char() (Details finden sich in der Dokumentation des Packages). Wer ebenfalls alle Felder mit "national character set" (NCHAR, NVARCHAR) in ihre "normalen Pendants" (CHAR,VARCHAR2) umwandeln möchte, findet auch dafür die passende Prozedur im Package.

Truncation

Als wir die zu konvertierenden Zeichen und das Tool CSScan behandelten, kamen wir auch auf die Kategorie Truncation zu sprechen: Fälle, in denen die konvertierten Inhalte "beschnitten" werden müssen, um noch in das Feld zu passen. Wer diese Aufgabe auf "später" verschieben möchte, findet sicher an der Prozedur iz_migration.truncate_long_fields() Gefallen: Sie schneidet nur das ab, was wirklich weg muss – speichert jedoch den ursprünglichen Inhalt in einem CLOB Feld einer speziellen Tabelle, zusammen mit dem Namen des Objektes und der zugehörigen RowID, sodass man sie später einfach wieder auffinden kann. Die Felder müssen auf jeden Fall vor dem Import "gekürzt" werden – da die entprechenden Zeilen sonst nicht importiert werden können. Die gerade genannte Weise erlaubt ein automatisches Kürzen, wobei man sich um evtl. notwendige Korrekturen später kümmern kann.

Datentransfer: Vergleich verschiedener Strategien

Drei mögliche Strategien kommen für die Datenmigration in Betracht. Folgende Tabelle soll eine kurze Übersicht geben, bevor wir diese Strategien im Detail betrachten:

Strategie Pros Cons
ALTER DATABASE Schnell und sicher Nur in sehr seltenen Fällen anwendbar
EXP/IMP Sicher und von Oracle unterstützt Langsam, benötigt eine relativ große Downtime
DBLink Copy Relativ kurze Downtime Nicht (offiziell) von Oracle unterstützt, evtl. nicht ganz "Error-proof"

Die ALTER DATABASE Methode

Sofern anwendbar, ist dies natürlich die erste Wahl: Von allen genannten Methoden ist sie nicht nur die schnellste ((fast gar) keine Downtime), sondern auch noch die sicherste und von Oracle voll unterstützt. Klingt soweit echt super – muss also einen Haken haben. Und hier kommt er: Nicht auf eine Latin-15 Datenbank anwendbar. Fällt also für unser Beispiel flach.

Export/Import

Was also empfiehlt Oracle für unseren Fall? Genau: Erstellen eines kompletten, konsistenten Datenbank Export, der anschließend in die (neu erstellte) UTF8 Datenbank importiert wird. Vom Prinzip her. Details dazu finden sich auf allen Oracle Seiten, daher brauchen wir hier nicht ins Detail zu gehen. Der Haken ist recht offensichtlich: Für einen kompletten und konsistenten Export muss die Datenbank ein Weilchen geschlossen werden. Und da in der Zeit zwischen dem Export und dem Öffnen der neuen Datenbank wohl kaum Änderungen am Inhalt erwünscht sein dürften, muss auch die alte Datenbank während des Imports in die neue Datenbank geschlossen bleiben. Abhängig von der Größe der Datenbank, kann dies schon Mal einen ganzen Tag (oder auch mehr) bedeuten.

Nicht immer ist es möglich, für einen ganzen (Arbeits-) Tag auf die Datenbank zu verzichten. Oftmals nicht einmal dann, wenn zumindest noch ein Nur-Lesen Zugriff möglich ist (was ja generell machbar wäre). Also brauchen wir eine Alternative. Es mag vielleicht überraschend klingen: Aber es geht sehr viel schneller, eine Tabelle über einen Datenbank-Link zu kopieren, als das Kopieren über Export/Import. Das Ergebnis dieser "Entdeckung" war das IZ_MIGRATION Package.

Die dahinter stehende Idee ist recht einfach: Da wir bereits die gesamte Struktur in der neuen Datenbank vorliegen haben, brauchen wir nur durch das Database Dictionary zu wandern nach dem Motto "Für jedes Schema – für jede Tabelle – INSERT INTO schema.tabelle SELECT * FROM schema.tabelle@dblink. Natürlich ist dabei noch einiges mehr zu beachten – was in der Dokumentation des Packages auch näher erklärt wird. Insbesondere:

Für Constraints, Trigger und Sequences enthält das Package natürlich die benötigten Prozeduren. Dabei werden – um die Datenbank möglichst schnell wieder verfügbar zu haben – die Constraints im Anschluss an den Kopiervorgang lediglich unvalidiert aktiviert (ENABLE NOVALIDATE) – wir gehen zunächst davon aus, dass die Daten vorher auch "valid" waren und müssen nur sicherstellen, dass nichts "invalides" hinzukommt. Nachdem die Datenbank wieder verfügbar ist, kann (und sollte) eine separate Prozedur dazu verwendet werden, sich (z. B. in einem nächtlichen Job) um die Validierung zu kümmern.

Auch um das Löschen der Indexe kann sich das Package kümmern. Zuvor sollten die Definitionen dieser Indexe jedoch gesichert werden, damit sie später wieder angelegt werden können – ein Skript, was genau dieses tut, findet sich am Ende dieser Seite.

Noch eine Warnung bzgl. der DBLink Copy Methode

Wie bereits eingangs erwähnt, wird diese Methode von Oracle weder "supported" noch gar empfohlen. Was vielleicht noch schlimmer ist: Sie wurde bislang auch keinem großen Test unterzogen (jaja, das Übliche: Wann bekommt man als Entwickler schonmal Feedback? Eigentlich nur, wenn jemand auf Probleme gestoßen ist). Zwar habe ich diese Methode (und natürlich das IZ_MIGRATION Package) erfolgreich bei einigen Migrationen eingesetzt (und dabei noch die eine oder andere Kleinigkeit korrigiert) – doch kann man daraus nicht auf eine Empfehlung zur Migration jedweder Produktions- Datenbank schließen. Wer es also aufgrund der damit verbundenen wesentlich kürzeren Downtime einsetzen, dabei aber die Risiken möglichst gering halten möchte, engagiert am besten den Entwickler selbst (also mich) für dieses Projekt. Auf jeden Fall sollte die ganze Migration zunächst anhand einer Testumgebung, die dem Produktivsystem so ähnlich wie möglich ist, getestet werden.

Skript zum Erstellen der CREATE INDEX Statements

-- Retrieving all index information (create-script) for non-pk and non-unique indexes
SET SERVEROUTPUT ON FEEDBACK OFF
SPOOL create_indexes.sql
DECLARE
  town VARCHAR2(40) := '0';
  CURSOR cur IS
    SELECT i.owner,i.index_name
      FROM dba_indexes i,dba_constraints c
     WHERE i.owner = c.owner(+)
       AND i.index_name = c.index_name(+)
       AND c.index_name IS NULL
       AND i.index_type NOT LIKE 'IOT%'
       AND i.index_type != 'LOB'
       AND i.owner NOT IN ('SYS','SYSTEM','CSMIG','CTXSYS','DBSNMP','DMSYS','EXFSYS',
                           'HR','IX','MDSYS','OE','OLAPSYS','ORDSYS','OUTLN',
                           'SH','SYSMAN','WKSYS','WK_TEST','WMSYS','XDB')
     ORDER BY i.owner,i.index_name;
  PROCEDURE print(line IN VARCHAR2) IS
    -- wrapper to dbms_output.put_line - work around the 255 characters limit
    pos NUMBER;
    FUNCTION strpos (str IN VARCHAR2,needle IN VARCHAR2,startpos NUMBER) RETURN NUMBER IS
      -- split string helper to procedure print
      pos NUMBER; strsub VARCHAR2(255);
      BEGIN
        strsub := SUBSTR(str,1,255);
        pos    := INSTR(strsub,needle,startpos);
        return pos;
      END;
    BEGIN
      dbms_output.put_line(line);
    EXCEPTION
      WHEN OTHERS THEN
        IF SQLERRM LIKE '%ORU-10028%' THEN
          pos := strpos(line,' ',-1);
	  print(SUBSTR(line,1,pos));
	  pos := pos +1;
	  print(SUBSTR(line,pos));
	ELSE
      dbms_output.put_line('-- *!* Problem in print() *!*');
	END IF;
    END;
BEGIN
  dbms_output.enable(1000000);
  FOR rec IN cur LOOP
    IF town != rec.owner THEN
      town := rec.owner;
      print('-- ===============================[ Schema '||town||' ]===');
    END IF;
    print(dbms_metadata.get_ddl('INDEX',rec.index_name,rec.owner)||';');
  END LOOP;
END;
/
SPOOL OFF

Fazit

Für die Migration einer Oracle-Datenbank stehen verschiedene Strategien zur Verfügung – von "sehr sicher" bis "sicher, aber schneller". Wir haben uns das im Detail angesehen. Viele Schritte in der Vorbereitungen sind zudem bei allen betrachteten Methoden identisch.

Welche Strategie nun auch zum Einsatz kommt: Das IZ_MIGRATION Package kann bei der Migration hilfreich sein, und die Lösung einiger Aufgaben wesentlich erleichtern. Auf IzzySoft.DE finden sich mehr Details – sowie eine Möglichkeit zum Download dieser und weiterer (Oracle-) Software.

2018-12-16