Arbeiten mit VARRAYs

Ein Varray ist ein weiterer Datentyp, der sowohl in der SQL- wie auch PLSQL-Welt von ORACLE existiert. Nochmal: PL/SQL-Tabellen gibt es nur in PLSQL, nested tables und varrays gibt es auch in SQL, so daß sich die beiden letzteren eignen, Daten dauerhaft in der Datenbank zu speichern.Der Unterschied zwischen Varray und nested table ist ein einfacher: Eine nested table hat alle Charaktereigenschaften einer normalen Tabelle, es handelt sich um eine Menge. Ein Varray hingegen ist eher zu vergleichen mit einer verketteten Liste. Die Position des Elements im Varray ist fix und wird mit Hilfe eines Index referenziert.

Weiter wichtig ist, dass wir in einer nested table keine obere Grenze für die Anzahl Werte haben, wogegen bei einem Array bereits beim Anlegen des TYPS(!) – und nicht erst beim Anlegen der Variablen – die maximale Anzahl von Listenelementen bestimmen müssen.

Ein Varray ist also sinnvoll, wenn Sie eine maximale Obergrenze von Elementen kennen, die das Varray aufnehmen soll. So weiss ich, dass bei einer Lottoziehung (6 aus 49) maximal sechs Zahlen ermittelt werden. Das spricht für den Einsatz eines Varrays. Dagegen spricht, dass andere Lottogesellschaften vielleicht mehr als sechs Zahlen ziehen und mein Datenmodell dort keine Gültigkeit mehr hat. Ein Varray ist die richtige Lösung, wenn es auch auf die Position des Listenelements in der Liste ankommt. Für stochastische Analysen ist es womöglich interessant zu wissen, in welcher Reihenfolge die Zahlen gezogen wurden. Eine nested table kennt keine Reihenfolge, damit könnte ich diese Forderung nicht abbilden. Ein anderes Beispiel: Eine Namensliste, in der die Vornamen einer Person entsprechend der Eintragung im Personalausweis aufgelistet werden sollen (erster Rufname, zweiter Rufname etc.), auch hier muss ich ein Varray nutzen, nur dort kann die Reihenfolge abgebildet werden.

Ähnlich, wie bei nested tables, müssen auch varrays initialisiert werden, bevor sie verwendet werden können. Ein Beispiel, in dem so ziemlich alles schiefgeht:

declare
  type namenarraytyp is varray(4) of varchar2(20);
  n1_array namenarraytyp;
  n2_array namenarraytyp := namenarraytyp('Hans','Detlef','Juergen',
                                               'Walther','Otto');
  n3_array namenarraytyp := namenarraytyp();
begin
  n1_array(1) := 'Helmut';
  n3_array(1) := 'Willi';
end;
/

Die Typdeklaration sieht maximal vier-elementige Namenslisten vor. n1_array wird deklariert, aber nicht initialisiert. Das ist noch kein Fehler, wir können diese Variable aber nicht nutzen. n2_array wird initialisiert, aber bei der Initialisierung habe ich fünf Elemente angegeben. Soviel kann das VArray nicht aufnehmen. Es wird die Ausnahme SUBSCRIPT_BEYOND_COUNT ausgelöst. n3_array wird bei der Deklaration initialisiert, die Liste enthält aber keine Elemente.

Das interessante passiert im ausführbaren Teil: Beide Anweisungen führen zu einem Fehler. Die Wertzuweisung an n1_array liefert die Ausnahme COLLECTION_IS_NULL, weil wir auf ein nicht initialisiertes VArray zugreifen,  die Wertzuweisung an n3_array liefert wieder ein SUBSCRIPT_BEYOND_COUNT. Das VArray wurde bei der Deklaration bereits initialisiert, aber trotzdem kann ich nichts zuweisen. Das ist ein wichtiger Unterschied z.B. gegenüber einer PL/SQL-Tabelle. Das VArray ist zwar in der Lage, bis zu vier Strings aufzunehmen, aber es existiert ja noch gar kein erstes Listenelement, in dem ich ‚Willi‘ speichern möchte. Aha! Wir brauchen also eine Methode, um überhaupt erstmal Listenelemente zu erzeugen und das geht mit EXTEND:

declare
  type namenarraytyp is varray(4) of varchar2(20);
  n_array namenarraytyp := namenarraytyp();
begin
  n_array.extend(2);
  n_array(1) := 'Willi';
  
  n_array.extend(2,1);
  dbms_output.put_line(n_array(4));
end;
/

In obigem Beispiel habe ich eine Variable n_array, die eine Liste von Werten aufnehmen kann, mit der Methode EXTEND erzeuge ich nun zwei leere Listenelemente (die haben den Wert NULL), und dann weise ich dem ersten dieser beiden Elemente den Wert ‚Willi‘ zu. Aber was passiert dann? Die zweite Variante EXTEND zu benutzen, ist die neuen Listelemente mit einem Wert zu initialisieren, den ein anderes Listenelement hat. In meinem Beispiel erweitere ich also mit dem zweiten EXTEND mein VArray auf seine maximale Größe von vier Listeinträgen und initialisiere den dritten und vierten Listeintrag mit dem Wert, der im ersten Element steht. Die Ausgabe auf dem Bildschirm zeigt Willi.

Welche Methoden gibt es noch:

  • COUNT, zählt die Elemente in einer Liste
  • DELETE, löscht alle Elemente einer Liste
  • EXISTS(x), prüft, ob das x-te Listenelement existiert
  • TRIM(x), entfernt die x letzten Elemente der Liste, versucht man Elemente zu entfernen, die gar nicht existieren, wird die Ausnahme SUBSCRIPT_BEYOND_COUNT ausgelöst.

Genau wie nested tables kann man auch VArrays als Typen innerhalb von ORACLE SQL anlegen und so Spalten von Tabellen diesen Typen zuweisen. Ein Select funktioniert genauso wie bei einer nested table mit Hilfe der TABLE-Funktion. Wollten wir ein Varray jedoch in irgendeiner Art und Weise verändern (so eine Art Insert, Update oder Delete einzelner Listenelemente), müssten wir Zugriff auf die oben genannten Methoden haben. Diese aber existieren in SQL nicht, sondern nur in PL/SQL. Ergo: Um VArrays zu ändern, muss erst das VArray aus der Tabelle in ein PL/SQL-Programm gelesen werden, dort verändert werden und dann wieder zurückgeschrieben werden. Dies ist ein erheblicher Mehraufwand, der bei Änderungen am Datenbestand zu Performance-Einbußen führt. Geht es in erster Linie um Select-Performance, können wir mit VArrays (genauso wie mit nested tables) ja notwendige Joins von zwei Tabellen verhindern, was durchaus zu Performance-Gewinnen führen kann.

Gegenüber dem Einsatz von Nested Tables (die eher zu einer Performance-Verschlechterung geführt haben), ist der Zugriff auf varrays in einer Tabelle interessant: Hier müssen intern nicht zwei Tabellen gejoint werden, sondern das varray ist wirklich Teil des Datensatzes. Greift ORACLE also auf den Datensatz zu steht gleichzeitig auch das Varray zur Verfügung. Damit ergeben sich – allerdings geringe – Vorteile gegenüber dem Join zweier Tabellen. Nachteil: Nur ein Select könnte mit dem Einsatz von Varrays beschleunigt werden, ein Update, Insert und Delete läuft viel langsamer. Warum? Nun, wir haben zum Verändern eines Varrays, anders als bei nested tables, keine DML Operationen zur Verfügung. Wir müssen erst das Varray lesen, dann die Elemente mit PL/SQL ändern, um das veränderte Varray wieder abzuspeichern. Hier ist also der Einsatz von PL/SQL unbedingt nötig, wogegen ich mich bei der Arbeit mit nested tables auch auf ORACLEs SQL-Syntax beschränken kann.

 

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.