Arbeiten mit Arrays in SQL, PL/SQL und Application Express - Teil I
Erscheinungsmonat |
APEX-Version |
Datenbankversion |
September 2013 |
alle |
ab 10.2 |
Die Arbeit mit Arrays ist für einen APEX oder PL/SQL Entwickler alltäglich - sie
werden immer wieder gebraucht. Und doch erscheint der Umgang mit Arrays in der Oracle-Datenbank
oft rätselhaft.
Es gibt
drei Array-Typen in der Oracle-Datenbank:
VARRAYs, Nested Tables und PL/SQL Assoziative Arrays.
Dieser Tipp gibt einen Einblick in die Arbeit mit Arrays in der Oracle Datenbank und stellt vor, was geht und was nicht.
Dabei wird auf SQL und PL/SQL fokussiert - in Application Express lässt sich
das hier vorgestellte natürlich beliebig nutzen: Beispiele sind Berichts-SQL oder PL/SQL in einem onSubmit-Prozess.
In diesem ersten Teil des Tipps stellen wir VARRAYs und Nested Tables,
die Array-Varianten, die sowohl in SQL als auch in PL/SQL bereitstehen, vor. Somit können diese Arrays auch als Datentypen
für Tabellenspalten oder in SQL-Abfragen bzw. in DML-Anweisungen genutzt werden. Die nur in PL/SQL verfügbaren
assoziativen Arrays werden im zweiten Teil behandelt.
VARRAY und SQL: SELECT, CREATE TYPE, CREATE TABLE
Ein VARRAY
(Varying Array) ist am ehesten mit den Arrays in Java oder C vergleichbar. Bei Definition
wird ein oberes Limit festgelegt, welches bis zu 2147483647 (231-1) Elementen reichen kann.
Nach Absetzen dieses Kommandos enthält das Datenbankschema einen neuen benutzerdefinierten
Datentypen - der in SQL und PL/SQL so verwendet werden kann, wie alle anderen. So kann eine
Tabelle mit einer Spalte dieses Typs angelegt und Werte eingefügt werden.
Auch mehrdimensionale Arrays sind möglich ...
Natürlich können die Werte auch wieder ausgelesen werden. Interessant ist hier
die Fähigkeit der SQL-Engine, Arrays mit der Funktion TABLE() als Tabellen aufzufassen.
Das folgende Beispiel zeigt das:
Die Funktion TABLE() fasst ein Array also wie eine Tabelle auf, die per Join
in die Abfrage eingebunden wird - das funktioniert auch für mehrdimensionale Arrays.
Das Gegenstück zu TABLE() ist die Funktion
COLLECT() - diese fasst einzelne Tabellenzeilen zu einem Array zusammen.
Interessant ist die Frage, wie das Array nun konkret gespeichert wird; immerhin
wird hier - in einer Tabellenspalte - eine ganze Liste abgelegt. Ein Blick in USER_TABLES
zeigt schnell, dass keine zweite Tabelle angelegt wurde. Sehr aufschlußreich ist dagegen
die Dictionary View USER_LOBS.
Das VARRAY wird also intern als Large Object - und damit als Ganzes abgelegt. Benötigt
das VARRAY weniger als 4000 Byte, so wird es, zusammen mit den anderen Spalteninhalten,
"in row" gespeichert. Größere VARRAYs werden in das separate LOB-Segment ausgelagert.
Daraus folgt, dass sich solche VARRAYs vor allem dann eignen, wenn eine Liste
bei Zugriffen normalerweise als Ganzes gelesen und dann weiterbearbeitet wird.
Mit nur einem einzigen Zugriff
hat man eine auch größere Werteliste komplett im Zugriff - im Vergleich zu einer "normalen"
1-n Tabellenbeziehung ist das kompakter und effizienter. Ein Beispiel dafür
ist der Oracle-Datentyp für Geodaten (SDO_GEOMETRY),
welcher die Koordinatenliste eines Linienzugs oder Polygons
als VARRAY speichert. Grund ist, dass diese stets als Ganzes gelesen wird - niemand
braucht nur einen Teil der Koordinaten eines Polygons.
Nested Tables und SQL: SELECT, CREATE TYPE, CREATE TABLE
Im Vergleich zu einem VARRAY ist eine Nested Table weniger ein Array als - wie der Name schon
sagt - eine Tabelle. Ein Datentyp für eine Nested Table wird wie folgt erzeugt.
Wiederum kann dieser neue benutzerdefinierte
Datentyp in SQL und PL/SQL verwendet werden - verwendet man ihn allerdings
für eine Tabellenspalte, so erlebt man zunächst eine Überraschung.
Da eine Nested Table keinerlei oberes Limit für die Anzahl an Einträgen hat,
kommt die Art der Speicherung als LOB (wie bei einem VARRAY) nicht in Frage. Die
Inhalte des VARRAY müssen in eine separate Tabelle abgelegt werden - und für diese
Tabelle muss ein Name angegeben werden.
Die SQL-Funktionen TABLE() und COLLECT() funktionieren analog zu VARRAYs - und
auch Nested Tables können mehrdimensional sein; es braucht dann eine Tabelle für jede Hierarchieebene.
Wiederum ist die Frage nach der konkreten Speicherung interessant - doch nun
ist die Situation eine andere: Schließlich haben wir den Namen NTAB_NAMELIST beim
CREATE TABLE-Kommando explizit angegeben. Es lohnt sich also, sich diese Tabelle
etwas näher anzusehen ...
Das Vorhandensein der Nested Table und die Verknüpfung zu ihrer Parent Table
lassen sich anhand des Data Dictionary sehr gut nachvollziehen - direkten Zugriff
auf die Nested Table erlaubt Oracle dagegen nicht. Der Grund dafür ist, dass diese
Tabellen in der Anwedung eben nicht direkt verwendet werden sollen - der Zugriff
soll - wie oben gezeigt - über die Parent-Tabelle und die SQL-Funktion TABLE() erfolgen.
Eine Nested Table eignet sich im Vergleich zu einem VARRAY daher sehr gut, wenn
bei Zugriffen nicht zwingend die ganze Liste gelesen werden soll. Dieser Umstand sei
an der folgenden Abfrage deutlich gemacht (Auch diese Abfrage funktioniert
sowohl mit Nested Tables als auch mit VARRAYs).
Hier wird also nur ein Teil der Liste benötigt - ist diese als VARRAY gespeichert,
so wird dennoch das ganze Array (LOB-Segment) gelesen und danach werden die
gewüschten Zeilen selektiert. Man kann sich schon denken, dass die Nested Table
(da die Inhalte in einer separaten Tabelle abgelegt werden) Möglichkeiten zur Optimierung bietet ...
Die Datenbank ist nun in der Lage, den Index zu nutzen - und so tatsächlich nur
die Teile der Liste zu lesen, die tatsächlich angefordert sind. Der Fairness halber
muss man aber auch zugeben, dass man nun auch über ein "normales" relationales Modell
nachdenken könnte.
Nested Tables und VARRAYs in PL/SQL
Auch in PL/SQL lassen sich VARRAYs und Nested Tables verwenden ...
Ein Array wird, analog zu objektorientierten Programmiersprachen, zunächst initialisiert. Hier
kann schon eine Werteliste übergeben werden. Wird die Werteliste weggelassen, so wird das
Array mit genau einem Wert initialisiert - und dieser Wert wird mit SQL NULL belegt. Bevor zusätzliche Einträge
mit l_stringarray(x) := '...' hinzugefügt werden können, muss das Array mit der
Funktion EXTEND erweitert werden. Der Parameter von EXTEND gibt an, für wieviele neue Einträge
Platz geschaffen werden soll. In obigem Beispiel hat das VARRAY eine Größe von drei Werten;
mit der Funktion COUNT lässt sich das feststellen. Die
Oracle-Dokumentation enthält in der PL/SQL Language Reference
Übersicht über die PL/SQL-Funktionen für Arrays.
In einer PL/SQL Prozedur, Funktion oder einem anonymen Block kann ein Array-Type auch
"lokal" definiert werden. Der Code dazu sieht wie folgt aus:
Der Type t_plsqlarray ist innerhalb
des anonymen Blocks definiert und nur
in diesem gültig. Für Tabellenspalten oder außerhalb des anonymen Blocks kann
er nicht genutzt werden. Das gilt analog
für Stored Procedures und Stored Functions. Ein in einer Package-Spezifikation
definierter Array-Typ kann, wie eine globale Package-Variable, in anderen PL/SQL-Objekten, nicht jedoch für Tabellendefinitionen genutzt werden.
Speziell für Nested Tables bietet PL/SQL noch "Multiset" Funktionen an, mit
denen SQL-Ähnliche Operationen wie UNION, INTERSECT oder DISTINCT auf PL/SQL
Nested Tables durchgeführt werden können. Mit Variablen vom Typ VARRAY funktionieren diese Funktionen dagegen nicht. Das folgende
Beispiel führt zwei in PL/SQL definierte Nested Table-Objekte zusammen und entfernt Duplikate.
Der Beispielcode produziert diese Ausgabe:
Fazit und weitere Informationen
VARRAYs und Nested Tables erlauben es, Arrays im SQL-Kontext zu nutzen. Es wird möglich, Arrays in einzelnen Tabellenspalten
abzulegen und mit SQL auf diese Werte zuzugreifen. Insbesondere, wenn Wertelisten
stets als Ganzes gelesen und geschrieben
werden sollen, kann das VARRAY eine Alternative zu einer separaten Tabelle sein. VARRAYs und Nested Tables lassen sich
auch in PL/SQL nutzen. PL/SQL Entwickler können jedoch auch mit assoziativen Arrays arbeiten - diese werden
im nächsten Community Tipp näher betrachtet. Mehr Informationen zu VARRAYs und Nested Tables finden Sie ...
Zurück zur Community-Seite
|