Generierung von XML aus relationalen Daten Uwe Hohenstein Generierung von XML aus relationalen Daten 1 Einleitung Die Relevanz von XML in der heutigen IT-Landschaft ist unbestritten und hat zu sehr vielfältigen Einsätzen geführt. Eine wichtige Rolle kommt dabei der adäquaten Speicherung und Verwaltung von XML-Dokumenten zu [Klettke & Meyer 2003]. Die Forschung und Entwicklung hat in diesem Bereich zur neuen Technologie der nativen XML-Datenbanken mit einer direkten und dedizierten Speicherung von XML geführt. Aus diesen Arbeiten sind Produkte wie Tamino von der Software AG [Schöning 2001] entstanden. Allerdings befinden sich große Mengen an Geschäftsdaten in »altbackenen« relationalen Datenbanken – im Prinzip entsteht hier aus XML-Sicht eine neue Ausprägung von Legacy-Daten. Eine Konvertierung dieser relationalen Daten nach XML ist sinnvoll: • Für einen Datenaustausch über XML: XML wird immer mehr als Datenaustauschformat verwendet, um Daten von einem Rechner zu einem anderen zu schicken. Das gilt natürlich auch für die in relationalen Datenbanken vorliegenden Daten. • Für eine Präsentation von relationalen Daten im Web, um beispielsweise Kataloge dynamisch zusammenzustellen und mit XSLT-Stylesheets kunden- oder endgerätespezifisch (HTML, WML etc.) aufzubereiten. Der Großteil dieser relationalen Daten wird weiterhin von Datenbankanwendungen benutzt, so dass sich eine Migration in eine XML-Datenbank verbietet. Somit ist häufig die Situation anzutreffen, dass für eine zuverlässige Datenspeicherung eine relationale Datenbank benutzt wird, wohingegen XML zur flexiblen Darstellung der Daten zum Einsatz kommt. Demzufolge ist eine Brücke nötig, um die Anforderungen XML-basierter Applikationen mit den Möglichkeiten eines RDBMS zu kombinieren. Die RDBMS-Hersteller haben frühzeitig reagiert: Es gibt viele proprietäre 52 Ansätze von namhaften Herstellern wie IBM, Microsoft oder Oracle, die SQL erweitern oder neuartige Abbildungsvorschriften definieren. Auf den Markt drängen zudem viele Drittanbieter mit Mapping-Tools wie Castor oder XML-DBMS [Bourret 2002], die auf beliebige relationale Datenbanken aufsetzen und sich somit eine Herstellerunabhängigkeit bewahren. Dieser Beitrag geht als Ergänzung zum Übersichtsartikel [Klettke & Meyer 2003] auf diese konzeptionellen Lösungen im Detail ein, wobei auf die Komposition von XML aus relationalen Daten fokussiert wird; die Speicherung von XML-Dokumenten in relationalen Strukturen wird nur kurz angerissen. Aufgrund der sehr verschiedenartigen Ansätze entsteht gerade im Rahmen der SQLStandardisierung der SQL/XML-Standard, der dem Wildwuchs Einhalt gebieten soll. Dieser wird anschließend beschrieben. 2 Proprietäre Herstelleransätze Der Ausgangspunkt für eine XML-Erzeugung sind bestehende Tabellen. Zur Illustration der verschiedenen Ansätze werden zwei Beispieltabellen benutzt: Person (Nr, Name, Plz, Ort, Strasse, Hausnr) Telefon (PNr, Telnr) Eine Fremdschlüsselbeziehung von Telefon.PNr zu Person.Nr drückt eine mengenwertige Eigenschaft aus: Eine Person besitzt mehrere Telefone. Abbildung 1 zeigt eine denkbare Repräsentation eines zugehörigen Datenbestands als XML-Dokument. Das <Personen>-Element umschließt den Dateninhalt, jedes Tupel wird als <Person>-Element mit der Nummer als XML-Attribut repräsentiert. Abweichend von der relationalen Darstellung werden im XML-Dokument die Adressteile zu einem neuen XML-Element <Adresse> strukturiert. Des Weiteren kommt es zu einer Schachtelung der Telefone als Wiederholungsgruppe <TelListe>. <Personen> <Person Nr="1"> <Name> Lucky Luke </Name> <Adresse> <Plz> 81730 </Plz> <Ort> München </Ort> <Strasse> Otto-Hahn-Ring </Strasse> <Hausnr> 6 </Hausnr> </Adresse> <TelListe> <Telefon> 089/123456 </Telefon> <Telefon> 0171/34567 </Telefon> </TelListe> </Person> <Person Nr="2"> ... </Person> </Personen> Abb. 1: XML-Darstellung Die nachfolgend diskutierten Ansätze werden grob in drei Kategorien unterteilt: a) Die beschreibungsbasierten Ansätze spezifizieren eine Abbildung von Tabellen nach XML. b) Die XML-basierten Verfahren definieren eine XML-Sicht auf relationale Daten. c) Die SQL-basierten Ansätze erweitern SQL um neue XML-Funktionen. 2.1 IBMs DB2 XML Extender Die hier diskutierte Funktionalität ist ab DB2 UDB Version 7.1 verfügbar. a) XML Collection Der beschreibungsbasierte Ansatz der XML Collections beruht auf einer so genannten Document Access Definition (DAD). Eine DAD ist eine XML-Datei mit einer Abbildungsbeschreibung. Im Prinzip lässt sich die Datei als ein XMLTemplate auffassen, in das relationale Daten als Textinhalte eingebettet werden. XML Collection bietet zwei Verfahren zur Konstruktion von XML: 1) Die Daten werden über eine SQL-Anfrage berechnet. 2) Komplette Tabellen, evtl. mit Tupelselektionen, bilden den Inhalt. Der Ansatz (1) ist im Gegensatz zu (2) aufgrund des »View Update«-Problems nur für lesende Zugriffe vorgesehen. Ein Beispiel einer DAD-Datei zeigt für (1), wie ein SQL-Statement Daten aus der Datenbank selektiert und im Sinne von Abbildung 1 als XML aufbereitet. Datenbank-Spektrum 7/2003 Generierung von XML aus relationalen Daten <?xml version="1.0"?> <!DOCTYPE DAD SYSTEM "/dxx_xml/test/dtd/dad.dtd"> <DAD> <validation>NO</validation> <Xcollection> <SQL_stmt> SELECT p.Nr, Name, Plz, p.Ort, p.Strasse, p.Hausnr, t.Telnr FROM Person p, Telefon t WHERE p.Nr=t.PNr ORDER BY p.Nr </SQL_stmt> <prolog>?xml version="1.0"? </prolog> <doctype>!DOCTYPE Test SYSTEM "test.dtd"</doctype> <root_node> <element_node name="Person"> <attribute_node name="Nr"> <column name="Nr"/> </attribute_node> <element_node name="Name"> <text_node> <column name="Name"/> </text_node> </element_node> <element_node name="Adresse"> <element_node name="Plz"> <text_node> <column name="Plz"/> </text_node> ... </element_node </element_node <element_node name="TelListe"> <element_node name="Telefon" multi_occurrence="YES"> <text_node> <column name="Telnr"/> </text_node> </element_node> </element_node </element_node> </root_node> </Xcollection> </DAD> Das <SQL_stmt>-Element der <Xcollection> spezifiziert zunächst die auszuwertende SQL-Anfrage. Die Anfrage hat gewisse Konventionen einzuhalten: • Die Reihenfolge der im SELECTStatement angesprochenen Tabellen entspricht dem hierarchischen Aufbau des zu erzeugenden Dokuments. • Die Namen der Ergebnisspalten müssen eindeutig sein, da die XML-Einheiten Bezug über diese Namen nehmen. Natürlich ist eine Umbenennung mit AS-Aliasen möglich. • Die Spalten müssen tabellenweise gruppiert sein, wobei jeweils ein lo- Datenbank-Spektrum 7/2003 kaler Identifikator der Gruppe vorangeht; dieser kann ein Primärschlüssel der entsprechenden Tabelle oder vom DBMS mit generate_unique() generiert sein. • Zwischen den Tabellen müssen JoinBedingungen bestehen. • Eine abschließende Sortierung über die Identifikatoren muss ebenfalls die XML-Hierarchie reflektieren. Nachfolgend wird die XML-Dokumentstruktur festgelegt. Hinter <root_ node> folgt die Wurzel des XML-Ergebnisdokuments. <element_node> kennzeichnet Elemente und <attribute_ node> legt Attribute fest. Im Prinzip handelt es sich hierbei um eine spezielle Schemabeschreibungssprache, die gewisse Grundzüge mit XML-Schema teilt, aber dennoch proprietär ist. Die Struktur des resultierenden XML-Dokuments ist mit diesen Mechanismen relativ frei definierbar. Zum Beispiel sind Elemente wie <Adresse> oder <TelListe> einfügbar. Das XML-Attribut multi_occurrence drückt bei <element_node name="Telefon"> eine Wiederholungsgruppe aus. Um das Anfrageergebnis dann im XML-Dokument »einzusortieren«, wird der Bezug zu den relationalen Daten hergestellt. <text_node> zeichnet einen Textknoten aus, der z.B. über <column name="..."> aus einer Ergebnisspalte aufgefüllt wird; der Bezug zur SQL-Anweisung erfolgt über Namensgleichheit. Bei XML-Attributen kann der Wert direkt über <column>, d.h. ohne Textknoten, berechnet werden. Das Ergebnis kann zusätzlich mit XSLT aufbereitet werden, indem ein <stylesheet>-Element ein Stylesheet registriert. Des Weiteren ist auch eine Validierung des Ergebnisses gegenüber einer DTD mit <validation> möglich. Beim Ansatz (2) werden statt Anfragen ganze Tabellen behandelt. Dadurch wird die Abbildung »umkehrbar«; eine Speicherung von XML-Daten wird möglich. Syntaktisch werden nun bei <root_node> zum Wurzelelement mit <RDB_node> alle zu berücksichtigenden Tabellen aufgelistet: <root_node> <element_node name="Person"> <RDB_node> <table name="Person"> <table name="Telefon"> <condition> Person.Nr=Telefon.PNr </condition> </RDB_node> ... Hiermit sind die beteiligten Tabellen festgelegt. <condition> drückt zudem eine Join-Bedingung aus, um die Tabellen in Beziehung zu setzen. Ist auch eine XML-Speicherung gewünscht, so ist zusätzlich für jede Tabelle eine Schlüsselangabe mit key="spalte1 spalte2 ..." erforderlich. Anschließend kann die innere Struktur des Wurzelelements festgelegt werden, indem Elemente und Attribute wie folgt Bezug zu Tabellen und Spalten nehmen: <RDB_node> <table name="Person"/> <column name="Nr"/> </RDB_node> Die Verarbeitung wird mit verschiedenen gespeicherten Prozeduren angestoßen, denen eine DAD-Datei dad mitgegeben wird und mit denen sich XML-Dokumente aus relationalen Daten gemäß der spezifizierten Abbildung komponieren lassen. So gibt z.B. dxxGenXMLCLOB (CLOB dad, ..., CLOB result, int maxRows, int numRows, long returnCode, varchar returnMsg) das resultierende XML-Do- kument als CLOB-Wert über den OUTParameter result zurück. Der Parameter maxRows ist nützlich, um das Ergebnis portionsweise abzuholen, indem die Anzahl Tupel beschränkt wird. Das verkürzt die Verarbeitungszeit. dxxShredXML(CLOB dad, CLOB xmlobj, long returnCode, varchar returnMsg) ist für die XML-Speiche- rung mit dem Ansatz (2) relevant. Das übergebene Dokument xmlobj wird gemäß der DAD dad zerlegt und feinstrukturiert in der Datenbank gespeichert. b) Xperanto / XTables Xperanto [Fan et al. 2002], neuerdings auch als XTables bezeichnet, ist eine Middleware, die sich noch im Prototypstadium befindet. Hier soll es dennoch erwähnt werden, da der Ansatz die zukünftige Entwicklung maßgeblich prägen wird. Das Prinzip besteht darin, eine XMLSicht für relationale Daten als XQueryAusdruck zu spezifizieren. Insofern ist der Ansatz der Kategorie (b) zuzuordnen. Die Sicht ist selbst wieder mit XQuery abfragbar, so dass das Konzept in sich abgeschlossen ist. 53 Generierung von XML aus relationalen Daten Da der XQuery-Anfragemechanismus eigentlich für XML und nicht für relationale Datenbanken gedacht ist, ist eine XML-Repräsentation der Datenbank als Ausgangspunkt nötig: <db> <Person> <row> <Nr> 1 </Nr> <Name> Lucky Luke </Name> <Plz> 81730 </Plz> <Ort> München </Ort> <Strasse> Otto-Hahn-Ring </Strasse> <Hausnr> 6 </Hausnr> </row> ... </Person> <Telefon> <row> <PNr> 1 </PNr> <Telnr>089/123456</Telnr> </row> <row> <PNr> 2 </PNr> <Telnr>0171/34567</Telnr> </row> ... </Telefon> </db> Diese Default-XML-Sicht ist implizit vorhanden und trägt den festen Namen default. Sie repräsentiert direkt die Tabellen und Tupel. Je Tabelle gibt es ein Element mit dem Tabellennamen, das wiederum die Tupel als <row>-Elemente enthält. Auf der Defaultsicht aufbauend können benutzerdefinierte XML-Sichten mit XQuery spezifiziert werden: CREATE VIEW vw AS ( <Personen> { for $p in view("default")/Person/row return <Person> <Nr> {$p/Nr} </Nr> <Name> {$p/Name} </Name> <Adresse> <Plz> {$p/Plz} </Plz> ... </Adresse> <TelListe> { for $t in view("default")/ Telefon/row where $p/Nr=$t/PNr return <Telefon> {$t/Telnr} </Telefon> } </TelListe> </Person> } </Personen> ) Der XQuery-Ausdruck baut die XMLStruktur aus Abbildung 1 auf. Mit view("default") wird innerhalb des 54 Ausdrucks Bezug auf die Defaultsicht default genommen. Mit for $p werden alle Person-Tupel durchlaufen, um <Person>-Elemente zusammenzustellen. Die innere Schleife for $t bestimmt die Telefone jeder Person $p. Ein return-Ausdruck liefert jeweils das Teilergebnis, wobei Ausdrücke wie {$p/Plz} ausgewertet werden. Diese View vw repräsentiert ein XML-Dokument, das nun ausgebbar ist. Derartige XML-Sichten lassen sich mit view("vw") wieder in XQuery-Anfragen nutzen, d.h., man bewegt sich nun in der XML-Welt und nicht mehr in SQL. Nichtsdestoweniger werden XQuery-Anfragen intern relational »ausgeführt«, d.h. in SQL übersetzt und an die SQL-Anfragebearbeitung weitergeleitet. 2.2 Microsoft SQL Server 2000 Die folgende Diskussion bezieht sich auf »XML for Microsoft SQL Server 2000 Web«, Release 2. Weitere Unterlagen finden sich unter den URLs http://msdn. microsoft.com/xml und http://msdn. microsoft.com/sqlxml. a) ADO.NET Der für die Microsoft-Welt nahe liegende Mechanismus nutzt das DataSet-Konzept aus ADO.NET. Ein DataSet lässt sich mit SQL füllen und ist dann im Prinzip eine tabellenartige Hauptspeicherrepräsentation einer oder mehrerer Ergebnistabellen. DataSet-Methoden ermöglichen das Produzieren von XML bzw. umgekehrt das Zerlegen von XML in Tabellen: • ds.WriteXml("outfile.xml") schreibt den Inhalt eines DataSet als XML in die angegebene Datei. • ds.ReadXml("infile.xml") liest ein XML-Dokument und speichert es feingranular als DataSet. Angenommen, ein DataSet ds mit dem Namen MyDataSet enthält den Inhalt der Tabellen Person und Telefon. ADO.NET nimmt dann folgende mit Xperanto vergleichbare, vorgegebene flache Darstellung des DataSet-Inhalts: <MyDataSet> <Person> <Nr> 1 </Nr> <Name> Lucky Luke </Name> <Plz> 81730 </Plz> ... </Person> ... <Telefon> <PNr> 1 </PNr> <Telnr> 089/123456 </Telnr> </Telefon> <Telefon> <PNr> 1 </PNr> <Telnr> 0171/34567 </Telnr> </Telefon> ... </MyDataSet> Je Tupel entsteht ein XML-Element mit dem Namen der Tabelle. Die Einflussnahme auf das XML-Format ist nur begrenzt. Über DataSet-Funktionalität lässt sich beispielsweise mit col.ColumnMapping=MappingType.Element/Attribute die XML-Repräsentation einer Spalte col als Element oder Attribut festlegen. Eine Schachtelung der Telefone innerhalb vom <Person> kann über DataSet-interne Beziehungen zwischen Tabellen erfolgen (rel.Nested=true). b) XML-Sichten mit annotierten Abbildungsschemata Wie in DB2 besteht auch hier die Möglichkeit, eine XML-Sicht im SQL-Server zu definieren. Das Prinzip ist jedoch ein anderes: Zum einen wird XML-Schema als Beschreibungssprache benutzt, zum anderen sind nur XPath- statt XQuery-Anfragen auf der XML-Sicht ausführbar. Ein Beispiel myxsd.xsd einer Sicht ist: <xsd:schema xmlns:xsd="http:// www.w3.org/2001/XMLSchema" xmlns:sql="urn:schemas-micro soft-com:mapping-schema"> <xsd:annotation> <xsd:appinfo> <sql:relationship name="Person_Telefon" parent="Person" parent-key="Nr" child="Telefon" child-key="PNr"/> </xsd:appinfo> </xsd:annotation> <xsd:element name="Person" sql:relation="Person"> <xsd:complexType> <xsd:sequence> <xsd:element name="Nr" type="xsd:integer" sql:field="Nr"/> <xsd:element name="Name" type="xsd:string" sql:field="Name"/> <xsd:element name="Adresse" sql:relation="Person"> <xsd:complexType> <xsd:sequence> <xsd:element name="Plz" type="xsd:integer" Datenbank-Spektrum 7/2003 Generierung von XML aus relationalen Daten sql:field="Plz"/> ... </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element ref="TelListe" sql:relationship= "Person_Telefon"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="TelListe"> <xsd:complexType> <xsd:element name="Telefon" type="xsd:string" maxOccurs="unbounded" sql:field="Telnr"/> </xsd:complexType> </xsd:element> </xsd:schema> Das XML-Schema legt die XML-Elemente als <element> und die Datentypen als <complexType> fest, um die Struktur aus Abbildung 1 zu definieren. sql-Annotationen spezifizieren den Zusammenhang zu Tabellen und Spalten: sql:relation= "..." bestimmt innerhalb von <xsd:element> eine Tabelle für ein Element, sql:field spezifiziert eine Spalte für innere Elemente oder Attribute. Schachtelungen entstehen mit sql:relationship: Das referenzierte Relationship wird in <xsd:annotation> als <sql:relationship name= .../> mit parent/child-Angaben unter <xsd:appinfo> definiert. Mit der Sicht bewegt man sich ausschließlich in der XML-Welt, so dass nun XPath-Anfragen formulierbar sind. Im einfachsten Fall wird mit einem leeren XPath-Ausdruck " " das gesamte Dokument zurückgegeben, aber auch Extraktionen sind möglich. Unterstützt wird nur eine XPath1.0-Untermenge, die nur einige Rekursionsachsen und keine arithmetischen und String-Operationen bietet. Folgendes Beispiel zeigt eine XPathAnfrage in ADO, welche alle Telefonnummern einer Person ausgibt: cmd.Dialect = "{EC2A4293-" + "E898-11D2-B1B7-00C04F680C56}" cmd.Properties ("Mapping Schema") = "myxsd.xsd" cmd.CommandText = "/Person[Nr='1']//Telefon" Über Properties wird der Bezug zur obigen XSD-Datei myxsd.xsd hergestellt, CommandText erhält den XPath. Das resultierende DataSet kann mit ADO.NET ausgegeben werden. Datenbank-Spektrum 7/2003 Eine XPath-Anfrage liefert nicht notwendigerweise ein gültiges XML-Dokument; es ist manchmal aufgrund einer fehlenden Wurzel nur ein Fragment. In diesem Fall kann in ADO eine umschließende Wurzel bereitgestellt werden. Änderungen am Dokument lassen sich gemäß der XML-Sicht über spezielle XML-Dokumente, »Updategrams«, ausführen, in denen mit <updg:before> und <updg:after> der Vorher- bzw. Nachherzustand beschrieben wird. Das Ergebnis ist ein relational geprägtes, flaches XML-Format, das direkt die Tabellenstruktur reflektiert. Bei Joins, wie im obigen Fall, kommt es dabei zu Redundanzen, die vom SQL-Ergebnis herrühren: Zu jedem Telefon werden die Personendaten des Besitzers wiederholt. AUTO vermeidet diese Redundanz, indem es eine Schachtelung einführt: <ROOT xmlns:updg="urn:schemamicrosoft-com:xml-updategram"> <updg:sync mapping-schema="myxsd.xsd"> <updg:before> <Nr> 1 </Nr> </updg:before> <updg:after> <Name> Ms. Marple </Name> </updg:after> </updg:sync> </ROOT> Die FROM-Liste gibt die Reihenfolge der Schachtelung vor. Auf diese Weise entsteht eine implizite Gruppierung, welche die beiden Telefon-Daten im <Person>-Element einbettet: Der <sync>-Teil stellt den Bezug zur XSD-Datei her. <before> qualifiziert die zu modifizierenden Tupel. Ist der <after>-Teil leer, so werden alle qualifizierten Tupel gelöscht. Ohne <before> wird <after> als Dokument aufgefasst und neu eingefügt. Mit <before> und <after> ist es eine Änderung. Hier wird im Tupel mit Nr=1 (before) der Name auf Ms. Marple (after) abgeändert. c) FOR XML-Klausel Die FOR XML-Klausel ist eine SQL-Erweiterung. Insofern gehört der Ansatz zur Kategorie (c). Da die Abarbeitung im Datenbankkern integriert ist, kann das DBMS Optimierungen bei der Auswertung vornehmen. Die FOR XML-Anweisung erweitert die SELECT-Grundform um: FOR XML RAW | AUTO | EXPLICIT Die RAW-Variante nimmt eine Standardumsetzung vor, die jedoch kaum beeinflussbar ist. Für jedes Tupel des Ergebnisses wird ein <row>-Element mit den SELECT-Spalten als Attribute generiert; Nullwerte führen zu komplett weggelassenen Attributen. Die Anfrage SELECT * FROM Person p, Telefon t WHERE p.Nr=t.PNr FOR XML RAW ergibt beispielsweise <row Nr="1" Name="Lucky Luke" ... PNr="1" Telefon="089/123456"> <row Nr="1" Name="Lucky Luke" ... PNr="1" Telefon="0171/34567"> SELECT * FROM Person p, Telefon t WHERE p.Nr=t.PNr ORDER BY p.Nr FOR XML AUTO ELEMENTS <Person> <Nr> 1 </Nr> <Name> Lucky Luke</Name> <Plz> 81730 </Plz> ... <Telefon> <PNr> 1 </PNr> <Telnr> 089/123456 </Telnr> </Telefon> <Telefon> <PNr> 1 </PNr> <Telnr> 0171/34567 </Telnr> </Telefon> </Person> ... Der Tabellenname wird jeweils als Elementname genommen. Der Zusatz ELEMENTS lässt die SELECT-Spalten zu inneren Elementen werden, wirkt aber nur pauschal für das komplette Ergebnis. Ohne ihn wären die Spaltenwerte wieder zu Attributen geworden: <Person Nr="1" Name="Lucky Luke" ...> <Telefon PNr="1"...> ... Bei der internen Bearbeitung wird für jedes Person-Tupel ein XML-Element erzeugt. Zu allen unmittelbar folgenden Ergebnistupeln zur selben Person werden die Telefon-Anteile eingebettet. Daher ist die Sortierung mit ORDER BY bedeutend, weil ansonsten die <Person>-Elemente zu einer Nr, jeweils mit den nachfolgenden Telefonnummern, verstreut auftreten würden. Auch bei AUTO ist das erzeugte XMLMuster noch recht starr. Beispielsweise ist keine Einfügung von zusätzlichen Strukturierungselementen wie <TelListe> oder <Adresse> möglich. Diese Defizite werden durch EXPLICIT behoben. EXPLICIT bietet die volle Flexibilität wie z.B. eine individuelle Wahl Attribut/Element oder Berechnun- 55 Generierung von XML aus relationalen Daten SELECT 1 AS Tag, NULL AS Parent, p.Nr AS "Person!1!Nr", p.Name AS "Person!1!Name", NULL AS "Adresse!2!Plz!ELEMENT", ..., NULL AS "TelListe!2", NULL AS "Telefon!3!Telnr" FROM Person p UNION SELECT 2 AS Tag, 1 AS Parent, NULL AS "Person!1!Nr", NULL AS "Person!1!Name", p.Plz AS "Adresse!2!Plz!ELEMENT", ... NULL AS "TelListe!2", NULL AS "Telefon!3!Telnr" FROM Person p UNION SELECT 2 AS Tag, 1 AS Parent, NULL AS "Person!1!Nr", NULL AS "Person!1!Name", NULL AS "Adresse!2!Plz!ELEMENT", ... p.Nr AS "TelListe!2", NULL AS "Telefon!3!Telnr" FROM Person p WHERE p.Nr = t.PNr SELECT 3 AS Tag, 2 AS Parent, NULL AS "Person!1!Nr", NULL AS "Person!1!Name", NULL AS "Adresse!2!Plz!ELEMENT", ... NULL AS "TelListe!2", t.Telnr AS "Telefon!3!Telnr" FROM Person p, Telefon t WHERE p.Nr = t.PNr ORDER BY [Person!1!Nr] FOR XML EXPLICIT • eine Spalte mit Namen Tag (Hierarchienummer) • eine Spalte Parent (Hierarchienummer des Vaterknotens) • weitere Spalten der vorgeschriebenen Form Element!Stufe!Name!Art Abbildung 2 zeigt eine Anfrage, die das Ergebnis aus Abbildung 1 generiert. Typischerweise werden mit einem UNION die Daten der einzelnen Hierarchiestufen zusammengefügt. Die Anfrage für diese an und für sich simple Aufgabe wird sehr komplex und fehleranfällig. Es ist auch nicht ausgeschlossen, dass ungültige XML-Dokumente produziert werden. Zum Verständnis des Prinzips soll die virtuelle Ergebnistabelle in Abbildung 3 betrachtet werden, die den Ausgangspunkt der Generierung bildet. Die als Tag und Parent benannten Spalten legen die PaVater-Sohn-Beziehung fest: rent=NULL zeigt eine Wurzel an, Tag=2 hat wegen Parent=1 den Vater 1 und so fort. Dies beeinflusst die XMLSchachtelung. Die restlichen Spalten definieren die XML-Elemente und ihre Werte, jeweils festgelegt durch die Adresspezielle Namensgebung: se!2!Plz!ELEMENT repräsentiert ein Element Adresse auf der zweiten Hierarchiestufe, dem ein Attribut Plz mitge- 56 string c = "Server=(local);Data" +"base=northwind;UID=sa;PWD=;"; SqlConnection adoConn = new SqlConnection(c); adoConn.Open(); SqlCommand adoCmd = new SqlCommand ("SELECT ... FOR XML RAW"); adoCmd.Connection = adoConn; DataSet ds = new DataSet(); ds.ReadXml(adoCmd.ExecuteXmlReader(), XmlReadMode.Fragment); ds.WriteXml(Console.Out); Neben der Einbettung in ADO.NET gibt es auch die Möglichkeit, XPath- und SQL-Anfragen mit einem Webbrowser auszuführen, beispielsweise als URL: http://IISServer/nwind?sql=SELECT+...+FOR+XML+AUTO?root=ERG Abb. 2: SQL-Anfrage mit FOR XML EXPLICIT gen, was aber durch komplexe und längliche Anfragen bezahlt werden muss. Der SELECT-Teil der SQL-Anfrage hat ein gewisses Format einzuhalten. Folgendes muss vorhanden sein: FOR XML-Anfragen lassen sich als SQL-Anfrage interaktiv ausführen oder in eine Programmierschnittstelle einbetten, z.B. mit ADO in C# : geben wird; der Wert berechnet sich aus der SELECT-Anweisung. TelListe!2 erzeugt nur ein Element ohne Textinhalt; der SQL-Wert von p.Nr ist irrelevant. Es entsteht je Tupel mit Parent=NULL ein XML-Element der Art: <Person> <Nr> 1 </Nr> <Name> Lucky Luke </Name> <Adresse> <Plz> 81730 </Plz> <Ort>München<Ort> ... </Adresse> <TelListe> <Telefon Telnr="089/123456"/> <Telefon Telnr="0171/34567"/> </TelListe> </Person> Ohne den Zusatz !ELEMENT werden Spaltenwerte wieder als Attribute repräsentiert. Andere Angaben sind: • XMLTEXT: Der Wert wird als literales XML dargestellt • CDATA übernimmt den Wert als CDATA-Abschnitt • ID für ID-Angaben und analog für IDREF(S) • HIDE unterdrückt einen Wert in der XML-Ausgabe HIDE ist z.B. sinnvoll, um über Spalten zu sortieren, die nicht Bestandteil des Ergebnisses werden sollen. Auch bei EXPLICIT muss wieder eine Sortierung dafür sorgen, dass die Söhne einer Stufe n unmittelbar ihrem Vater (der Stufe n-1) folgen. Durch den Parameter root=ERG bekommt das generierte XML-Dokument eine umschließende Wurzel ERG zugewiesen. Ohne diese Wurzel würde das Ergebnis im Fall mehrerer Ergebnistupel ungültiges XML ergeben. Sowohl die FOR XML-Anweisung als auch die XPath-Ausdrücke für XMLSichten können in XML-Templates eingebunden werden, die über HTTP oder WebDAV aufrufbar sind. Hinzu gesellt sich die Möglichkeit einer XSLT-Nachbearbeitung. Das folgende Template bettet ein FOR XML- (query) und ein XPathAnfrageergebnis (xpath-query) ein: <ROOT xmlns:sql="urn:schemasmicrosoft-com:xml-sql"> <SQL-Ergebnis> <sql:query> FOR XML-Anfrage </sql:query> </SQL-Ergebnis> <XPath-Ergebnis> <sql:xpath-query mapping-schema="myxsd.xsd"> XPath-Ausdruck </sql:xpath-query> </XPath-Ergebnis> </ROOT> Der Abruf des Templates ist dann: http://IISServer/nwind?template=<ROOT>+xmlns:sql=...</ROOT> Ein solches Template kann auch in ADO ausgeführt werden. 2.3 Oracle9i Oracle bietet seit der Version 8i eine umfangreiche XML-Unterstützung wie z.B. das XML SQL Utility (XSU). Mit der Datenbank-Spektrum 7/2003 Generierung von XML aus relationalen Daten Tag Parent Person!1!Nr ... Adresse!2!Plz!Element ... TelListe!2 Telefon !3!Telnr 1 2 2 3 3 1 ... NULL 1 1 2 2 NULL 1 NULL NULL NULL NULL 2 NULL 81730 NULL NULL NULL ... NULL NULL 1 NULL NULL NULL NULL NULL 089/123456 0171/34567 Abb. 3: Virtuelle Ergebnistabelle Version 9i kommt ein neuer Objekttyp XMLType zur Abstraktion hinzu, der als Spaltenwertebereich zur Aufnahme von XML-Dokumenten konzipiert ist und die Speicherungsform wählbar und transparent macht. Spezielle SQL-Funktionen können XMLType-Dokumente verarbeiten, wobei XPath-Funktionalität geboten wird. Das Buch »Webanwendungen entwickeln mit Oracle9i« [Hohenstein & Schmatz 2003] geht im Detail auf die gebotene Unterstützung ein. a) XML SQL Utility XSU stellt eine programmatische Schnittstelle bereit, mit der sich Anfrageergebnisse als XML-Dokument präsentieren lassen. Es ist in Java geschrieben und als Programmierschnittstelle über die bereitgestellten Java-Klassen OracleXMLQuery und OracleXMLSave benutzbar. Es stehen aber auch Pakete DBMS_XMLQuery und DBMS_XMLSave in der proprietären Sprache PL/SQL zur Verfügung. Oracle verfolgt in XSU einen anderen beschreibungsbasierten Ansatz als DB2 und SQLServer: Die Ergebnisstruktur und Schachtelung wird mit objektrelationalen Konzepten aufgebaut. Die Umsetzung der objektrelationalen Strukturen nach XML erfolgt dann gemäß folgender »kanonischer« Abbildung: Ergebnis Tupel Spaltenwert innere Tabelle <ROWSET> <ROW> <Spaltenname> <Spaltenname> für die Kollektion und je Eintrag <Spaltenname_ITEM> Objekt rekursive Behandlung der Struktur Referenz <Spaltenname REFTYPE="REF T"> mit OID Tab. 1: Kanonische Abbildung Ein Beispiel veranschaulicht das Prinzip: SELECT p.Nr, p.Name, Adresse_Typ(Plz,Ort,Strasse, Hausnr) AS Adresse, Datenbank-Spektrum 7/2003 CAST(MULTISET(SELECT Telnr FROM Telefon WHERE PNr=p.Nr) AS Telefon_Tab) AS TelListe FROM Person p Hier wird in SQL eine geschachtelte Struktur aufgebaut. Der Objekttypkonstruktor Adresse_Typ strukturiert die Einzelspalten Plz, Ort etc. zu einem Objekt. MULTISET erstellt eine innere Tabelle TelListe, d.h., der Wert ist selbst wieder eine Tabelle. Voraussetzung ist folgender Objekttyp und TABLE-Typ: CREATE TYPE Adresse_Typ AS OBJECT (Plz NUMBER, Ort VARCHAR(30), Strasse VARCHAR(30), Hausnr VARCHAR(5)); CREATE TYPE Telefon_Tab AS TABLE OF VARCHAR(20); Die gemäß der kanonischen Abbildung erzeugte XML-Struktur lautet: <?xml version="1.0"?> <ROWSET> <ROW num="1"> <NR> 1 <NR> <NAME> Lucky Luke </NAME> <ADRESSE> <PLZ> 81730 </PLZ> <ORT> München </ORT> <STRASSE> Otto-Hahn-Ring </STRASSE> <HAUSNR> 6 </HAUSNR> </ADRESSE> <TELLISTE> <TELLISTE_ITEM num="1"> 089/123456 </TELLISTE_ITEM> <TELLISTE_ITEM num="2"> 0171/34567 </TELLISTE_ITEM> </TELLISTE> </ROW> <ROW num="2"> ... </ROW> ... </ROWSET> Die in Oracle SQL aus flachen Tabellen erzeugte geschachtelte Struktur wird in ein geschachteltes XML-Dokument umgesetzt. ROWSET und ROW sind dabei Tags, die die gesamte Ergebnismenge bzw. jedes einzelne Tupel auszeichnen. Jedes Tupel wird über ein num-Attribut nummeriert. Jeder Wert eines Tupels wird ebenfalls zu einem Element und mit einem Tag umschlossen, das nach dem Namen der Spalte bzw. einem Alias in der SELECT-Anweisung benannt ist. Ein Alias ist nützlich, wenn komplexe Ausdrücke oder aggregierende Funktionen in SQL benutzt werden. Ein Alias kann dann dem Tag einen sprechenden Namen geben. Auch aus XML-Sicht ungültige Bezeichner lassen sich so vermeiden. Nullwerte erscheinen nicht im Ergebnis; das komplette Element zum Attribut verschwindet in diesem Fall. Um einen Spaltenwert als XML-Attribut zu übernehmen, kann ein mit einem '@' beginnender Alias genommen werden. Ein Alias '@Nr' würde zum Beispiel die Spalte Nr zu einem Attribut des Tags <ROW num="1" NR="1"> machen. Geschachtelte Strukturen, gegeben durch Objekte oder innere Tabellen, werden entsprechend aufgelöst. Bei objektwertigen Spalten wird die innere Struktur direkt über Tags reflektiert. Zum Beispiel untergliedert sich <ADRESSE> in <PLZ>, <ORT>, <STRASSE> und <HAUSNR> entsprechend der Struktur des Objekttyps Adresse_Typ. Die innere Tabelle wird gemäß dem Alias als <TELLISTE>-Element dargestellt. Die einzelnen Einträge der inneren Tabelle werden mit <TELLISTE_ITEM> markiert, d.h., dem Listen-Tag wird ein _ITEM nachgestellt. Oracles objektrelationale Erweiterungen bieten mächtige Möglichkeiten, die Struktur des XML-Dokuments zu beeinflussen. Oracle benutzt also keine spezielle Abbildungssprache für die Umsetzung von Tabellendaten nach XML. Die Verarbeitung kann in Java und PL/SQL erfolgen. Die Java-Klasse OracleXMLQuery besitzt Konstruktoren, denen neben einer JDBC-Datenbankverbindung eine SQL-Anfrage übergeben wird: //JDBC -Connection öffnen: Connection conn = Drivermanager.getConnection ("jdbc:oracle:thin:@myhost" + 57 Generierung von XML aus relationalen Daten ":1521:mydb","SCOTT","TIGER"); OracleXMLQuery q = new OracleXMLQuery(conn, "SELECT ... FROM ..."); org.w3c.dom.Document domDoc = q.getXMLDOM(); q.close(); Das Connection-Objekt legt die Datenbank mydb auf dem Rechner myhost sowie das Schema SCOTT/TIGER fest. Das XML-Ergebnis einer Anfrage kann über verschiedene get-Methoden abgeholt werden. So liefert getXMLDOC das Anfrageergebnis in einer DOM-Repräsentation, so dass die Funktionalität des Oracle XML Developer’s Kit (XDK) nutzbar ist, um z.B. XSLT-Transformationen durchzuführen, eine DTD zu erzeugen oder nachfolgend Parser anzuwenden. Die Funktion getXMLString gibt alternativ das Ergebnis als Zeichenkette aus. Den Methoden kann ein Node-Parameter mitgegeben werden, der zur Wurzel des Ergebnisdokuments wird; das eigentliche Ergebnis wird zum Teilbaum. In XSU ist auch ein XSLT-Prozessor integriert, der dazu benutzt werden kann, XSLT-Transformationen auf dem Ergebnis anzuwenden. Die Klasse OracleXMLQuery stellt diverse Methoden bereit, mit denen sich die Schreibweise beeinflussen lässt. Mit setRowTag(String tag) wird z.B. <ROW> zu <tag> umbenannt. setRowSetTag(String tag) wirkt analog auf <ROWSET>. Ebenso lassen sich die numAttribute des <ROW>-Tags und der inneren Kollektionen mit setRowldAttrName(String n) bzw. setCollIdAttrName(String n) umbenennen. Wird bei den Prozeduren null als Parameter angegeben, so wird kein Element bzw. Attribut erzeugt. Weitere Methoden sind hilfreich, um ein Anfrageergebnis stückweise in XMLDokumente umzuwandeln. Mit setMaxRows(anz) wird jeweils die Anzahl der zu verarbeitenden Tupel festgelegt. Mit setSkipRows(i*anz) können dann im i-ten Schleifendurchlauf die bereits bearbeiteten Sätze übersprungen werden, bevor mit restartQuery() die Anfrage erneut ausgeführt wird. Der Mechanismus ist nützlich, um Hauptspeicher zu sparen, da nicht mehr das komplette Dokument auf einmal erstellt werden muss. Mit XSU hat ein Programmierer auch umgekehrt die Möglichkeit, XML-Doku- 58 mente zu speichern. XML-Dokumente, die der kanonischen Abbildung entsprechen, lassen sich mit der Java-Klasse OracleXMLSave wieder feingranular auf mehrere Tabellen verteilen. Nicht auftretende Tags werden als NULL gespeichert. Das Speichern setzt voraus, dass die XML-Dokumente ein festes Schema, z.B. durch eine DTD gegeben, besitzen. OracleXMLSave sav = new OracleXMLSave(conn,"MyPerson"); sav.insertXML(sav.getUrl("f")); sav.close(); Beim Konstruktoraufruf ist die Tabelle oder Sicht anzugeben, auf die das Dokument zu verteilen ist. Wenn sich das XML-Dokument über mehrere Tabellen erstreckt, dann muss eine Objektsicht über diese Tabellen definiert werden, z.B. zur Speicherung des oben generierten XML-Dokuments eine Objektsicht MyPerson: CREATE VIEW MyPerson AS SELECT von oben Beim Speichern erfolgt damit die Aufteilung des Dokuments auf die der Sicht zugrunde liegenden Tabellen Person und Telefon. In der Regel ist ein INSTEAD OF-Trigger für die View notwendig, um die Wirkung von Einfügen, Ändern und Löschen eindeutig zu definieren. Ein großer Vorteil von XSU ist, dass sich auch Dokumentteile effizient löschen und ändern lassen. Das übergebene Dokument dient als Muster für die Suche, d.h., das Dokument legt die Vergleichswerte für eine WHERE-Bedingung fest. Zudem beschreibt das Dokument bei Einfügungen und Änderungen den neuen Zustand des Dokuments. Einzelheiten hierzu sind in [Hohenstein & Schmatz 2003] nachzulesen. b) XMLType-Sichten Auch in Oracle9i besteht die Möglichkeit, XML-Sichten über relationale Daten zu legen. Ähnlich wie im SQL-Server wird hierzu ein XML-Schema mySchema.xsd angelegt, um die Struktur der XML-Sicht zu definieren. Das Schema kann dann wie folgt zur Sichtdefinition benutzt werden: CREATE VIEW MyView OF XMLType XMLSCHEMA "http://www.oracle.com /xsd/mySchema.xsd#Person" WITH OBJECT IDENTIFIER (extract (SYS_NC_RowInfo$, '/Person/@Nr') .getNumVal()) AS SELECT SYS_XMLGen(VALUE(p)) FROM MyPerson p Die Sicht besteht aus XMLType-Objekten. XMLType ist ein vordefinierter Objekttyp, der XML-Dokumente repräsentiert, deren Struktur durch das über XMLSCHEMA referenzierte Schema definiert wird. Hinter # steht der Einstiegspunkt zur Validierung. Jedes XMLType-Objekt erhält einen Objektidentifikator, dessen Wert hier mit extract über einen XPath '/Person/@Nr' extrahiert wird. SYS_NC_RowInfo$ bezeichnet dabei die implizite XMLType-Spalte in der Sicht. Zur Berechnung der XML-Sicht aus relationalen Daten wird hier die XML-generierende SQL-Funktion SYS_XMLGen genutzt (siehe unten). Oracles SQL definiert zwei Funktionen existsNode und extract, die XPath-Funktionalität für XMLType bieten und zur Verarbeitung der XML-Sicht benutzt werden können: SELECT extract(VALUE(v), '/Person/Adresse/Plz') FROM MyView v WHERE existsNode(VALUE(v), '/Person//Telefon') existsNode prüft die Existenz des über den XPath /Person//Telefon adressierten Elements (d.i. ein Telefon), während extract ein Fragment (die Plz) über XPath aus dem Dokument extrahiert. Beide Funktionen erwarten ein XMLType-Objekt als ersten Parameter, das hier als Sichtobjekt mit VALUE(v) berechnet wird. c) SQL-Funktionen und Pakete Das PL/SQL-Paket DBMS_XMLGEN stellt eine Möglichkeit dar, um XML-Dokumente aus den Ergebnissen objektrelationaler SQL-Anfragen zu generieren. Beim Lesen aus der Datenbank ist die XML-Struktur bis auf kleinere Unterschiede durch die bei XSU diskutierte kanonische Abbildung gegeben. Als wesentlicher Unterschied besteht aber keine Möglichkeit, mit einer Umkehrabbildung XML-Dokumente in die Datenbank zu schreiben bzw. zu löschen und zu ändern. Als Vorteil hat DBMS_XMLGEN eine bessere Performanz als XSU zu verbuchen, da die Funktionalität direkt im Datenbankkern integriert ist. Das folgende Beispiel zeigt das Prinzip von DBMS_XMLGEN anhand eines PL/ SQL-Programms: Datenbank-Spektrum 7/2003 Generierung von XML aus relationalen Daten DECLARE ctx DBMS_XMLGEN.ctxHandle; result CLOB; BEGIN ctx := DBMS_XMLGEN.newContext ('SELECT ...'); result := DBMS_XMLGEN.getXML(ctx); DBMS_XMLGEN.closeContext(ctx); END; DBMS_XMLGEN.ctxHandle ist ein vordefinierter Objekttyp. Die Funktion newContext legt zu einer SQL-Anfrage eine Instanz ctx an. Über sie erfolgt anschließend die gesamte Steuerung. Mit getXML lässt sich dann zu ctx bzw. zur dahinter stehenden Anfrage ein XMLDokument als CLOB abrufen. Wie in XSU stehen in DBMS_XMLGEN diverse Funktionen wie setSkipRows oder setRowTag zur Verfügung. Die allein stehenden SQL-Funktionen SYS_XMLGEN und SYS_XMLAGG bieten ebenfalls eine Möglichkeit, XMLDokumente in SQL-Anweisungen, insbesondere interaktiv unter SQL*Plus, aus Anfrageergebnissen zu erzeugen. SYS_XMLGEN hat eine ähnliche Wirkung wie DBMS_XMLGEN. Die Struktur des erzeugten XML-Dokuments ist wieder durch eine kanonische Abbildung gegeben und kann durch die objektrelationalen Konzepte beeinflusst werden. Ein Aufrufbeispiel ist: SELECT SYS_XMLGEN(Adresse_Typ (Plz,Ort,Strasse,Hausnr)) FROM Person WHERE Nr = 1 Je Tupel wird ein XMLType-Objekt mit folgendem Inhalt konstruiert: <?xml version="1.0"?> <ROW> <PLZ> 81730 </PLZ> <ORT> München </ORT> ... </ROW> Die Wirkung ist, als würde 'SELECT Adresse_Typ(...) FROM ...' der Prozedur DBMS_XMLGEN.newContext übergeben und ausgeführt werden. Anders als dort kann hier nur ein einzelner Ausdruck in der SELECT-Liste auftreten. Dennoch ist es wieder möglich, ein komplexes Objekt, bestehend aus eingebetteten Objekten, Referenzen und geschachtelten Tabellen, zusammenzustellen. Auf diese Weise lassen sich ungeachtet der Restriktion komplexere XML-Ergebnisstrukturen aufbauen. Wie bei DBMS_XMLGEN werden durch die kanonische Abbildung die Spaltennamen direkt als Tags übernom- Datenbank-Spektrum 7/2003 men. Im Fall von Funktionsaufrufen oder komplexen SELECT-Ausdrücken wird <ROW> zum Tag. Mit einem zweiten SYS_XMLGEN-Parameter kann aber in SYS_XMLGEN(Adresse_Typ(...), SYS_XMLGENFORMATTYPE. createFormat('ADR')) das <ROW>-Tag zu <ADR> umbenannt werden. Ein weiterer optionaler Parameter erlaubt Verarbeitungsinstruktionen, was insbesondere für eine nachfolgende Stylesheet-Transformation nützlich ist. Damit kann jederzeit reines HTML oder ein anderes Format erzeugt werden. Während SYS_XMLGEN jedes Ergebnistupel separat bearbeitet – je Tupel entsteht ein XMLType-Objekt –, werden durch SYS_XMLAGG mehrere Tupel zu einem einzigen XMLType aggregiert. Alle Ergebniswerte sind aneinander gehängt und mit dem Tag <ROWSET> umschlossen. SYS_XMLAGG ist eine aggregierende Funktion im Sinne von AVG oder COUNT. Als solche kann sie auch mit GROUP BY kombiniert werden: SELECT SYS_XMLAGG(SYS_XMLGEN(Nr)) FROM Person GROUP BY Name Diese Anfrage gruppiert gleiche NameWerte. Je Name wird die Menge der Personennummern berechnet und für jede Nummer SYS_XMLGEN(Nr) ausgewertet. Das Ergebnis wird anschließend mit SYS_XMLAGG aggregiert, so dass sich ein einziges Ergebnistupel ergibt: <ROWSET> <NR> 10 <NR> 20 </ROWSET> <ROWSET> <NR> 15 <NR> 25 <NR> 35 </ROWSET> </NR> </NR> </NR> </NR> </NR> 2.4 Abbildungswerkzeuge von Fremdanbietern Neben den RDBMS-Herstellern hat sich eine Tool-Landschaft mit diversen Abbildungswerkzeugen für relationale Datenbanken etabliert, die sich ebenfalls mit der XML-Generierung und -Speicherung befassen. Diese Werkzeuge haben den Vorteil der Datenbankunabhängigkeit, was der Portabilität zugute kommt. Auch hier gibt es wieder mehr oder minder komplexe Abbildungssprachen unterschiedlicher Funktionalität. Als frei verfügbarer Kandidat sei XML-DBMS [Bourret 2002] erwähnt. Es hat die nützliche Eigenschaft, die hierarchische Struktur eines XML-Dokuments, insbesondere die Reihenfolge der Kinder auf einer Hierarchiestufe wie auch die Character-Daten und Attributwerte, zu erhalten; viele Ansätze haben hiermit Probleme. Das heißt, Dokumente lassen sich in derselben Form rekonstruieren, wie sie gespeichert wurden. Das kann aber zu Lasten der Performanz gehen. Castor, ein weiteres Beispiel, basiert auf einer objektrelationalen Abbildung, bei der zunächst Objekttypen über relationale Tabellen spezifiziert werden, die anschließend mit einer Standardabbildung als XML ausgegeben werden. Manche Werkzeuge wie Allora bieten eine komfortable grafische Unterstützung zur Erstellung einer Abbildungsvorschrift. In der Regel handelt es sich bei diesen Ansätzen um eine Middleware, die zwischen einer Applikation und einer Datenbank vermittelt. Demzufolge muss eine Applikation geschrieben werden, um den Mechanismus zu nutzen. 3 Der SQL/XML-Standard Der SQL-Standard untergliedert sich inzwischen in mehrere Teile der »ISO/IEC JTC 1/SC32/WG3 Database Languages – SQL«. Im Jahr 2000 ist der Teil »Part 14: XML-Related Specifications (SQL/ XML)« hinzugekommen, der von der offenen SQLX-Gruppe, jetzt zu H2.3 Task Group umbenannt, initiiert wurde. IBM und Oracle sind maßgebliche Treiber dieser Gruppe. Unter www.sqlx.org finden sich Einzelheiten zur Gruppe und zum aktuellen Stand. Die SQL/XML-Standardisierung ist immer noch im Fluss. Nichtsdestoweniger weisen IBMs DB2 und Oracle9i bereits erste Implementierungen auf. Grundlage des Standards ist ein neuer Datenbankdatentyp »XML«, der in Oracle beispielsweise XMLType heißt. Dieser kann als üblicher Wertebereich für Tabellenspalten genommen werden; die Werte repräsentieren dabei XML-Dokumente. Im Folgenden wird für diese Werte die Bezeichnung XML-Objekt gewählt. 3.1 SQLX-Funktionen SQL/XML definiert im Wesentlichen neue SQL-Funktionen, die SQLX-Funktionen, mit denen sich XML-Dokumente 59 Generierung von XML aus relationalen Daten als Objekte des Typs XML zusammenstellen lassen. Die Funktion XMLElement erzeugt aus SQL-Ausdrücken ein XML-Element, ggf. mit Attributen versehen. SELECT XMLElement(NAME "Person", XMLAttributes(Nr AS id), Name, Ort) FROM Person Je Tupel entsteht ein XML-Objekt der Art: <Person id="10"> Lucky Luke München </Person> Das hinter NAME angegebene Tag wird als Elementname genommen und bildet die Wurzel jedes generierten XML-Objekts. Die SQL-Ausdrücke Name und Ort werden zum Inhalt des Elements ausgewertet. Mit XMLAttributes(...) lassen sich SQL-Ausdrücke zum erzeugten Element als Attributwerte hinzufügen. Im Normalfall wird der Ausdruckbezeichner, also z.B. der Spaltenname, als Attributname verwendet. Mit AS lässt sich aber auch explizit ein Alias wie id vergeben. Wird ein Ausdruck zu einem Nullwert ausgewertet, so entfällt das entsprechende Attribut bzw. Element. XMLElement-Funktionen können verschachtelt werden, um eine geschachtelte XML-Struktur zu erzeugen: SELECT XMLElement(NAME "p:Person", XMLAttributes('http://www.' || 'hr.com /hr' AS "xmlns:p"), Nr AS PNr), XMLElement(NAME "Name",Name), XMLElement(NAME "Plz",Plz)) FROM Person Der Ausdruck zur XML-Attributberechnung ist in diesem Fall eine URL als Zeichenkettenkonstante. Normalerweise ist ein Escape-Mechanismus wirksam, der in XML ungültige Zeichen durch den Hexcode des Zeichens ersetzt. Eine wichtige Ausnahme ist das Symbol ‘:’, das unverändert bleibt. Auf diese Weise lassen sich Namespaces durch entsprechende Aliase p:Person setzen. Allerdings findet keine Überprüfung der Namespace-Präfixe statt; der Namespace muss also nicht korrekt eingerichtet und referenziert sein. Das Ergebnis ist hier: <p:Person xmlns:p="http:// www.hr.com/hr" PNr="10"> <Name> Lucky Luke </Name> <Plz> 81730 </Plz> </p:Person> Eine weitere SQLX-Funktion XMLForest ist für strukturierte Record-Ausga- 60 ben nützlich. Die Funktion liefert einen Wald von XML-Elementen. Die Ausdrücke werden wie üblich ausgewertet und bilden jeweils ein Element, das ggf. mit einem Alias bezeichnet wird. Das Ergebnis hängt für jedes Tupel alle diese Elemente aneinander. Der Aufruf XMLForest(Nr,Name) erzeugt somit folgenden Wald als XML-Objekt: <Nr> 10 </Nr> <Name> Ms. Marple </Name> Um aus diesem Fragment ein gültiges XML-Dokument zu erhalten, lassen sich anschließend durch XMLElement die mit XMLForest erzeugten Elemente zu <Person> ...</Person> umklammern: SELECT XMLElement("Person", XMLForest(Nr,Name)) FROM Person XMLForest ähnelt in der Wirkung einer XMLElement,...,XMLEleFolge ment. Der Unterschied besteht darin, dass diese mehrere XML-Objekte zurückgibt, während es bei XMLForest nur ein Ergebniswert ist. XMLConcat(XML,...,XML) konkateniert die übergebenen XML-Argumente zu einem einzigen XML-Objekt. Das Ergebnis ist ein Fragment. Ein NULLWert als Argument wird einfach ignoriert. Die Sequenz wird typischerweise als Folge von XMLElement-Aufrufen berechnet. Mit der XMLConcat-Funktion lässt sich XMLForest simulieren, nur dass hier die XML-Elemente individuell spezifiziert werden: SELECT XMLConcat( XMLElement(NAME "Nr",Nr), XMLElement(NAME "Name",Name)) FROM Person erzeugt je Tupel wieder: <Nr> 10 </Nr> <Name> Ms. Marple </Name> Um mehrere Tupel zu einem einzigen XML-Objekt zu verknüpfen, gibt es die aggregierende Funktion XMLAgg. XMLAgg wird typischerweise zusammen mit GROUP BY benutzt, wodurch sie auf jede Gruppe angewendet wird. SELECT XMLElement(NAME "Person", XMLAttributes(Name AS "PName"), XMLAgg(XMLElement(NAME "Nr",Nr) ORDER BY Nr)) FROM Person p GROUP BY Name; erzeugt: <Person PName="Ms.Marple"> <Nr> 10 </Nr> <Nr> 20 </Nr> </Person> ... Die Gruppierung über GROUP BY Name liefert jeweils Gruppen von Tupeln mit demselben Name-Wert. Für jede Gruppe berechnet XMLAgg die Konkatenation der XMLElement(NAME "Nr",Nr)-Werte. Mit einer optionalen ORDER BY-Angabe innerhalb XMLAgg wird je Gruppe eine Reihenfolge definiert und eingehalten. Schließlich gibt es noch eine mächtige generische Funktion, die die Funktionalität der anderen Funktionen umfasst. XMLGen('<Person Nr="{$nr}"> ' || '<Name> {$n} </Name> ' || '</Person>', Nr AS nr, Name AS n) erzeugt je Tupel: <Person Nr="10"> <Name> Ms.Marple </Name> </Person> Der erste Ausdruck ergibt die XMLStruktur als Zeichenkette. Die Platzhalter {$x} werden durch die Auswertungen der nachfolgenden SQL-Ausdrücke substituiert. Der Zusammenhang erfolgt über Namensgleichheit. Platzhalter sind nicht nur für Elementinhalte und Attributwerte möglich, auch Element- und Attributnamen lassen sich im Gegensatz zu XMLElement erzeugen. 3.2 Standardkonformität Standards haben es so an sich, dass sie von Herstellern zwar erfüllt werden, jeder Hersteller es sich aber nicht nehmen lässt, »nützliche« Zusatzfunktionalität anzubieten. Oracle zum Beispiel stellt zusätzliche Funktionen bereit, die mit den speziellen SQL-Erweiterungen harmonieren. Hierzu zählt XMLSequence(XMLType), das die Top-Level-Elemente des XMLTypeObjekts als Sequenz bestimmt. Zur Erinnerung sei erwähnt, dass XMLType Oracles Implementierung des XML-Objekttyps ist. Gültige XML-Dokumente bzw. XMLType-Objekte haben genau eine Wurzel, so dass dann nur eine einelementige Sequenz entsteht; XMLType-Fragmente, wie sie z.B. durch Oracles SQL-Funktion extract berechnet werden, besitzen jedoch mehrere Wurzeln. Das Ergebnis ist eine Sequenz von XML-Objekten als Instanz des Typs XMLSequenceType, der äquivalent zu VARRAY OF XMLType ist. Die Funktion ist nützlich, um Wiederholungsgruppen in XML wie die Telefonliste zu entschachteln. Sei Doks eine Tabelle mit einer XMLType-Spalte xml: Datenbank-Spektrum 7/2003 Generierung von XML aus relationalen Daten SELECT id, VALUE(t) FROM Doks d, TABLE(XMLSequence(extract (d.xml, '//Telefon'))) t; extract liefert mehrere Telefon-Elemente, die mit XMLSequence zu einem VArray zusammengefügt werden. TABLE erlaubt dann, eine Variable t an die einzelnen Telefon-Werte zu binden. XMLConcat kann ebenfalls ein XMLSequenceType verarbeiten, indem es die einzelnen Werte zu einem XMLType konkateniert. Eine andere Oracle-Funktion XMLColAttVal nimmt mehrere Parameter der Form Ausdruck [ AS Alias ], um daraus einen Wald von <column>Elementen als XMLType-Objekt zu erzeugen, indem jeder Ausdruck zu einem <column>-Element ausgewertet wird: <column name="Alias_1"> Wert_1 </column> <column name="Alias_n"> Wert_n </column> Die Aliase bzw. die Ausdruckbezeichner werden somit zu Attributwerten. 3.3 Zeichenumsetzung Bei der Umsetzung von SQL- in XMLBezeichner besteht das Problem, dass in SQL mehr Zeichen als in XML erlaubt sind. Zum Beispiel sind in SQL möglich: • Identifier, die mit XML beginnen (XML ist in XML reserviert) • ‘:’(das in XML für Namespace-Angaben verwendet wird) Um gültiges XML zu erhalten, ist eine Umsetzung der SQL-Identifier in Unicode nötig. Bei der Auswertung findet folgende implizite Transformation statt: • Wird ein Attribut als Spaltenname genommen, so wird der Name »fully escaped«. • Ein Alias wird »partially escaped«. Tabelle 2 zeigt ein paar Beispiele für Umsetzungen von Sonderzeichen in beiden Varianten: SQLIdentifier fully escaped partially escaped Name NAME NAME "Name" Name Name "Name v" Name_x0020_v Name_x0020_v "Name_v" Name_x005F_v Name_x005F_v "n:Name" n_x003A_Name n:Name xmldata _xFFFF_xmldata xmldata Tab. 2: Umsetzungen von Sonderzeichen Datenbank-Spektrum 7/2003 Entsprechend gibt es eine Umkehrabbildung von XML zu SQL, die z.B. _x0020_ in ein Leerzeichen umwandelt. Auch Werte werden implizit umgesetzt: • TIME'11:35:16' zu 11:35:16 • Eine Zeichenkette 'Schulz' zu Schulz (ohne Anführungszeichen) • INTERVAL'1:17' zu PT1H17M Der SQL/XML-Standard spezifiziert des Weiteren, wie z.B. eine Tabelle (oder ein ganzes Datenbankschema) in XML-Darstellung repräsentiert wird, ähnlich zur Defaultsicht von DB2, oder wie ein XML-Schema inkl. Datentypen abgeleitet wird. 4 Zusammenfassung Dieser Beitrag behandelte die Generierung von XML aus relationalen Daten. Was angesichts der nativen XML-Datenbanken überflüssig erscheint, hat durchaus seine Berechtigung: Heutzutage liegen viele Daten in relationalen Datenbanken vor – und werden es auch noch in Zukunft. Die RDBMS-Hersteller bieten zur Gewinnung von XML aus relationalen Daten vielfältige Unterstützung. Es zeigt sich aber, dass die Ansätze sehr heterogen sind: Das Spektrum reicht von SQL-Erweiterungen über Abbildungsbeschreibungen und XML-Schema-Annotationen bis hin zu XML-Sichten. Ergänzt wird die Palette durch Produkte von Nicht-DBMS-Herstellern. Inzwischen entsteht im Rahmen der SQL-Standardisierung der SQL/XMLStandard, der SQLX-Funktionen einführt. Alle namhaften Hersteller werden sicherlich im Laufe der Zeit diesen Standard unterstützen, teilweise tun sie es bereits. Wahrscheinlich wird auch IBMs Xperanto die XML-Unterstützung in relationalen Datenbanken prägen, da es sich XQuery zunutze macht, die XMLund relationale Welt koppelt und auf diese Weise eine gute Flexibilität bietet. Sicherlich wird die Bedeutung von XQuery in diesem Umfeld steigen. In diesem Beitrag wurde nur der Teilaspekt der Generierung betrachtet. Relationale DBMS bieten in der Regel auch die umgekehrte Möglichkeit, XML-Dokumente feinstrukturiert auf Tabellen zu verteilen, was kurz angerissen wurde. Ein anderer Ansatz legt ein XML-Dokument komplett in ein uninterpretiertes CLOB ab. In diesem Fall steckt die XML-Funktionalität in der Freitextsuche, die z.B. bei DB2 durch den Text-Extender oder bei Oracle durch die OracleText-Cartridge gegeben ist [Hohenstein & Schmatz 2003]. 5 Literatur [Bourret 2002] Bourret, R.: XML-DBMS: Middleware for Transferring Data between XML and Relational Databases. http://www.rpbourret.com/xmldbms/index.htm, 2002. [Eisenberg & Melton 2001] Eisenberg, A.; Melton, J.: SQL/XML and the SQLX Informal Group of Companies. ACM SIGMOD Record, Vol. 30, No. 3, Sept. 2001. [Fan et al. 2002] Fan, C.; Funderburk, J.; Lam, H.-I. et al.: Xperanto: Bridging Relational Technology and XML. http://www7b.software. ibm.com/dmdd/library/techarticle/0203shekita/ 0203shekita.pdf. 2002. [Hohenstein & Schmatz 2003] Hohenstein, U.; Schmatz, K.-D.: Webanwendungen entwickeln mit Oracle9i – Java, XML, JDBC & SQLJ, Oracle9i Application Server. dpunkt.verlag, 2003. [Klettke & Meyer 2003] Klettke, M.; Meyer, H.: Speicherung von XML-Dokumenten – eine Klassifikation. In: Datenbank-Spektrum, Heft 5/2003, S. 40-50. [Schöning 2001] Schöning, H.: XML-Datenbanken. In: Datenbank-Spektrum, Heft 1/2001, S. 33-34. [SQLX 2002] ISO/IEC JTC 1/SC32/WG3 Database Languages – SQL – Part 14: XML-Related Specifications (SQL/XML) – Final Committee Draft, H2-2002-063, WG3:ICN-011, März 2002. Dr. Uwe Hohenstein studierte Informatik an der TU Braunschweig. Dort promovierte er 1989 mit Arbeiten auf dem Gebiet des konzeptionellen Datenbankentwurfs. Seit 1990 ist er im Bereich Forschung und Entwicklung der Siemens AG tätig, wo er sich als Projektleiter um die effiziente und produktive Nutzung aktueller Datenbanktechnologien in konkreten Anwendungen kümmert. In diesem Umfeld sammelte er viele Erfahrungen bei der Auswahl, der Bewertung und dem Einsatz von relationalen, objektorientierten und objektrelationalen Datenbanksystemen. Seine Erfahrungen hat er in vielen Publikationen und auf diversen Fachtagungen vorgestellt. Dr. Uwe Hohenstein Siemens AG München CT SE 2 81730 München [email protected] http://www.siemens.com 61