XQuery - "SQL für XML" "The best way to explain XQuery is to say that XQuery is to XML what SQL is to database tables." • • • • • • • • • • • • • Was ist XQuery? ... Datentypen & Typumwandlung (Type Casting) Operatoren Arithmetik Konstruktoren FLWOR Ausdrücke Verbundoperationen (Joins) Gruppierungen If-then-else-Anweisung Quantoren Namensräume Vordefinierte Funktionen Benutzerdefinierte Funktionen Was ist XQuery? … ‐ Anfragesprache für XML ‐ XQuery ist das, was SQL für Datenbanken ist ‐ XQuery baut auf XPath Ausdrücken auf ‐ wird von allen größeren Datenbanksystemen unterstützt ‐ ist ein W3C Standard Voraussetzungen sind Kenntnisse von: • • • • XML XPath 2.0 XML Namespaces XML Schema Kommentare in XQuery‐Anfragen werden in runder Klammer mit Doppelpunkt geschachtelt: (: Dies ist ein XQuery-Kommentar :) Eine XQuery Anfrage ist ein Ausdruck, der eine Sequenz von atomaren Werten oder XML‐Fragmenten liest und auch zurückgibt. ‐ String‐Werte werden wie in anderen Sprachen auch in einfache Hochkomma 'string' oder doppelte Hochkomma "string" geschrieben ‐ wie bei XML muss auf Groß‐ und Kleinschreibung geachtet werden! Alle Schlüsselwörter werden klein geschrieben XQuery ist eine funktionale Sprache, d.h. sie besteht aus Ausdrücken. Dabei kann ein Ausdruck folgende Form haben: • • • • • • • Pfadausdrücke Element‐Konstruktoren FLWOR‐Ausdrücke Listen‐Ausdrücke fallbedingte Ausdrücke quantifizierte Ausdrücke Datentyp‐Ausdrücke Ausdrücke sind beliebig ineinander schachtelbar und werden dabei relativ zu einem Kontext ausgewertet (Namensraum, Kontext‐Position, Kontext‐Knoten/Wert etc.). Anfragenstruktur: Eine Anfrage besteht aus einem fakultativen Anfragen‐Prolog und einem Anfragen‐Körper. Der Prolog wird nur benötigt, wenn der Körper von Namensräumen, Schemas oder Funktionen abhängt. In ihm werden die Deklarationen definiert, die die Umgebung für den Anfrage‐Körper bilden. Der Anfrage‐Körper besteht aus dem Ausdruck, der den Wert der Anfrage repräsentiert. Datenmodell: Die "Sequenz" bildet das zentrale Konstrukt und besteht aus einer Aneinanderreihung von atomaren Werten oder Knoten mit beliebiger Anzahl von Einträgen, wobei keine Schachtelung erlaubt ist. Sequenzen sind geordnet und die Sequenzeinträge sind damit hinsichtlich ihrer Position unterscheidbar; Duplikate sind möglich. Somit lassen sich einzelne Einträge einer Sequenz auf folgende Weise abrufen: let $sequenz := ("a","b","c") return <buchstabe>{ $sequenz[2] }</buchstabe> Ausgabe: <buchstabe>b</buchstabe> Der Ausdruck: (1, "x", (), (<a/>, 4), "s") ist äquivalent zu:(1, "x", <a/>, 4, "s") Eine Sequenz mit einem Eintrag ist äquivalent zum Eintrag selbst, d.h. (5) ist äquivalent zu 5. Die einfachste Anfrage in XQuery ist ein XPath 2.0 Ausdruck! doc("buchliste.xml")//buch/titel[contains(.,"XQuery")] XQuery 1.0 & XSLT 2.0 XSLT 20 und XQuery 1.0 wurden von zwei Arbeitsgruppen in enger Zusammenarbeit entwickelt, so dass sie in ihrer Funktionalität zu einem hohen Grad überlappen. Sie haben viele gemeinsame Konzepte, wie das zugrunde liegende Datenmodell, beide enthalten XPath 2.0 komplett als Untersprache, ... Beide Sprachen richten sich an unterschiedliche Bedürfnisse, die in unterschiedlichen Nutzergemeinschaften existieren. Daher konzentrieren sich viele nur auf eine der beiden Sprachen und vernachlässigen die andere. Einige Aufgaben können beide Sprachen sehr gut erfüllen, für andere ist jedoch die eine Sprache der anderen vorzuziehen. Daher ist die Kenntnis in beiden Sprachen wichtig, um abschätzen zu können, welche sich für ein gegebenes Problem besser eignet. Der Fakt, dass sie sich im Konzept sehr ähneln, heißt, dass man die andere dadurch auch leichter verstehen kann. Welche Sprache einem persönlich besser gefällt, muss jeder selbst entscheiden, da schwer entscheidbar ist, welche für einen selbst besser geeignet ist. Eine Kombination beider Sprachen ist bisher nicht vorhanden oder wenn doch nur in dem jeweils darauf spezialisierten Produkt. Somit muss man sich entscheiden, ob meine eine XML‐Datei nur mit XQuery, nur mit XSLT oder aber erst die eine Sprache verwendet und die andere nochmals auf das erzeugte Ergebnisdokument. Datentypen & Typumwandlung (Type Casting) XQuery unterstützt alle vordefinierten Datentypen vom XML-Schema (Namensraumpräfix: xs). Darüber hinaus wird die Menge der Datentypen durch "unspezifische Typen" erweitert. Diese finden Verwendung, wenn keine eindeutige Zuordnung möglich ist. Konstante Werte können als • • • Literale Kontruktor‐Funktionen Cast geschrieben werden. Literale String Literale werden von doppelten oder einfachen Hochkommas begrenzt. Steht ein String in doppelten Hochkommas, kann er einfach Hochkommas enthalten; zwei benachbarte doppelte Hochkommas werden als ein doppeltes Hochkomma interpretiert. Dies gilt analog für einfache Hochkommas. Bsp.: "ein String" 'ein String' "Dies ist ein 'String'!" 'Dies ist ein "String"!' "ein "" oder ' grenzt ein String ab" 'ein " oder '' grenzt ein String ab' --> string-literal.xq --------------------------------------------------------------------------declare boundary-space preserve; <text>{ for $i in ("ein String", 'ein String', "Dies ist ein 'String'!", 'Dies ist ein "String"!') return <string>{ $i }</string> }</text> ----- Ergebnis ----<text> <string>ein String</string> <string>ein String</string> <string>Dies ist ein 'String'!</string> <string>Dies ist ein "String"!</string> </text> --------------------------------------------------------------------------- Desweiteren gibt es 3 Arten numerischer Literale: Jede Zahl kann optional ein Vorzeichen (+/‐) haben. • • • xs:integer ‐ besteht nur aus Ziffern Bsp.: 4, -6, +3, ... xs:decimal ‐ besteht nur aus Ziffern und einem Dezimalpunkt Bsp.: 1.23, -1.23, ... xs:double ‐ besteht nur aus Ziffern, einem optionalen Dezimalpunkt und einem e oder E Bsp.: 1.2E3, -1.2E3, ... Konstruktor­Funktion Bsp.: xs:integer("12"), xs:gYear("2007"), ... Cast Bsp.: "12" cast as xs:integer, "2007" cast as xs:gYear, ... Beispiele für Casting (: Casting von xs:gYear nach xs:integer nicht möglich, aber über xs:gYear --> xs:string --> xs:integer gleiches gilt für die Gegenrichtung :) <casting> <gYear_to_integer>{ let $x := "2003" cast as xs:gYear let $y := $x cast as xs:string let $z := $y cast as xs:integer return $z }</gYear_to_integer> <string_to_boolean>{ for $x in ("true","false") return <bool>{$x cast as xs:boolean}</bool> }</string_to_boolean> <integer_to_boolean>{ for $x in (0,1) return <bool>{$x cast as xs:boolean}</bool> }</integer_to_boolean> </casting> Operatoren • • • • • • • • skalare Vergleichsoperatoren: eq, ne, lt, le, gt, ge allgemeine Vergleichsoperatoren: =, !=, <, <=, >, >= Knoten Vergleichsoperatoren: is, isnot ‐ vergleicht die Identität zweier Knoten Reihenfolge‐Operatoren: $knoten1 << $knoten2 bzw. $knoten2 >> $knoten1 Negator: not Logische Operatoren: and, or Komma‐Operator: , ‐ verknüpft zwei Werte um eine Sequenz zu formen (2, 5),(8) ==> (2, 5, 8) to‐Operator: to ‐ gibt eine Sequenz zurück, die aus allen Integern zwischen dem linken und dem rechten Operanden (inklusive) besteht (2 to 4) ==> (2, 3, 4) Beispiele Beim Vergleich zweier Sequenzen mit allgemeinen Verlgeichsoperatoren erhält man true, sobald auch nur ein Paar(a,b) diese Bedingung erfüllt, wobei a ein Eintrag der ersten Sequenz und b ein Eintrag der zweiten Sequenz ist. Es wird somit ein existenzieller Vergleich durchgeführt. let $a := (1,2,3), $b := (3,4,5), $c := (4,5,6) return <res> <a_eq_b> { $a = $b } </a_eq_b> <a_ne_b> { $a != $b } </a_ne_b> <a_gt_b> { $a > $b } </a_gt_b> <a_lt_b> { $a < $b } </a_lt_b> <a_eq_c> { $a = $c } </a_eq_c> <a_ne_c> { $a != $c } </a_ne_c> <a_gt_c> { $a > $c } </a_gt_c> <a_lt_c> { $a < $c } </a_lt_c> </res> <res> <a_eq_b> <a_ne_b> <a_gt_b> <a_lt_b> <a_eq_c> <a_ne_c> <a_gt_c> <a_lt_c> </res> true </a_eq_b> true </a_ne_b> false </a_gt_b> true </a_lt_b> false </a_eq_c> true </a_ne_c> false </a_gt_c> true </a_lt_c> Mit skalaren Vergleichsoperatoren lassen sich Werte vergleichen. Nutzt man diese zum Vergleichen von Sequenzen tritt ein Fehler auf! Vergleich zweier Knoten: let $a $b $c $d let $x return <a> <!-<aa> <ab> <ac> <ad> <!-<aa> <ab> <ac> <ad> </a> := := := := := (<a>XQuery</a>), (<a>XQuery</a>), (<b>XQuery</b>), (<a>XML</a>) ($a, $a, $b, $c, $d) Identitätsvergleich --> { $x[1] is $x[2] } </aa> { $x[1] is $x[3] } </ab> { $x[1] is $x[4] } </ac> { $x[1] is $x[5] } </ad> Wertevergleich --> { $x[1] = $x[2] } </aa> { $x[1] = $x[3] } </ab> { $x[1] = $x[4] } </ac> { $x[1] = $x[5] } </ad> <a> <!-<aa> <ab> <ac> <ad> <!-<aa> <ab> <ac> <ad> </a> Identitätsvergleich --> true </aa> false </ab> false </ac> false </ad> Wertevergleich --> true </aa> true </ab> true </ac> false </ad> Es ist eindeutig erkennbar, dass mit ‘is‘ bzw. ‘is not‘ ein Identitätsvergleich zweier Knoten durchgeführt wird, wobei die allgemeinen Operatoren nur die Werte vergleichen! Arithmetik • Übliche Operationen: o o o o o • Aggregatfunktionen: o o o o o • Addition: + Subtraktion: - Multiplikation: * Division: div Modulo: mod Summe: sum Durchschnitt: avg Anzahl: count Maximum: max Minimum: min Operationen auf Sequenzen: o o o Vereinigung: union Durchschnitt: intersect Differenz: except Beispiele <aggregate> <plus>{ 5 + 2 }</plus> <minus>{ 5 - 2 }</minus> <mal>{ 5 * 2 }</mal> <div>{ 5 div 2 }</div> <mod>{ 5 mod 2 }</mod> </aggregate> <aggregate> <plus>7</plus> <minus>3</minus> <mal>10</mal> <div>2.5</div> <mod>1</mod> </aggregate> let $x := (9,7,3,6,7,3,21,11,8,3,6,15) return <aggregate> <sum>{ fn:sum( $x ) }</sum> <avg>{ fn:avg( $x ) }</avg> <count>{ fn:count( $x ) }</count> <max>{ fn:max( $x ) }</max> <min>{ fn:min( $x ) }</min> <aggregate> <sum>99</sum> <avg>8.25</avg> <count>12</count> <max>21</max> <min>3</min> </aggregate> </aggregate> let $a := <a/>, $b := <b/>, $c := <c/> let $seq1 := ($a, $b), $seq2 := ($a, $b), $seq3 := ($b, $c) return <res> <seq1_union_seq2>{ $seq1 union $seq2 }</seq1_union_seq2> <seq2_union_seq3>{ $seq2 union $seq3 }</seq2_union_seq3> <seq1_intersect_seq2>{ $seq1 intersect $seq2 }</seq1_intersect_seq2> <seq2_intersect_seq3>{ $seq2 intersect $seq3 }</seq2_intersect_seq3> <seq1_except_seq2>{ $seq1 except $seq2 }</seq1_except_seq2> <seq2_except_seq3>{ $seq2 except $seq3 }</seq2_except_seq3> </res> <res> <seq1_union_seq2> <b/> <a/> </seq1_union_seq2> <seq2_union_seq3> <c/> <b/> <a/> </seq2_union_seq3> <seq1_intersect_seq2> <b/> <a/> </seq1_intersect_seq2> <seq2_intersect_seq3> <b/> </seq2_intersect_seq3> <seq1_except_seq2/> <seq2_except_seq3> <a/> </seq2_except_seq3> </res> Konstruktoren Elementkonstruktoren wie <Element>...</Element> stehen für sich selbst. Ausdrücke, die zwischen geschweiften Klammern stehen, werden ausgerechnet und das Ergebnis eingefügt. berechnete Elementkonstruktoren werden wie folgt notiert: element $ename { attribute $aname { $value }, $content } $ename steht hierbei für den Elementnamen, $aname für den Names des Attributs, $value für den Attributwert und $content für den Elementinhalt. Beispiele element kunde <kunde/> element kunde { attribute kunden_nr { 123 }, "Gerd Gans" } <kunde kunden_nr="123">Gerd Gans</kunde> bzw. <kunde>{ attribute kunden_nr { 123 }, "Gerd Gans" }</kunde> <kunde kunden_nr="123">Gerd Gans</kunde> element kunde { attribute kunden_nr { 123 }, element vorname { "Gerd" }, element nachname { "Gans" }, element adresse { element strasse { "Musterweg 3a" }, element ort { attribute plz { 01234 }, "Musterdorf" } } } <kunde kunden_nr="123"> <vorname>Gerd</vorname> <nachname>Gans</nachname> <adresse> <strasse>Musterweg 3a</strasse> <ort plz="1234">Musterdorf</ort> </adresse> </kunde> FLWOR-Ausdrücke FLWOR-Ausdrücke bilden die Basis für Anfragen auf XML-Datenbanken. Das Akronym FLWOR (gesprochen wie engl.: flower) steht für: • • • • • for: Iteration durch Eingabesequenz, Variablenbindung bei jedem Durchlauf let: Variablendeklaration und Wertzuweisung (keine Iteration!) where: Filter (mit for verwendet) order by: Sortieren der Werte return: Ergebniskonstruktion Ein FLWOR-Ausdruck muss mindestens eine let- oder for-Klausel enthalten, sowie eine return-Klausel for- und let-Klauseln können beliebig oft und in beliebiger Reihenfolge stehen. Bei der where-, order by- und return-Klausel ist die Reihenfolge zu beachten! FLWOR-Ausdrücke sind analog zu SELECT-FROM-WHERE in SQL. for - Iteration über eine/mehrere Sequenz/en von Werten, über die eine/mehrere Variable(n) aufgezählt werden soll(en) for $i in (1 to 3) (: bzw.: for $i in (1,2,3) :) return <tupel>{$i}</tupel> Ausgabe: <tupel>1</tupel> <tupel>2</tupel> <tupel>3</tupel> Weitere Beispiele: for $b in doc("buchliste.xml")/buchliste/buch let $a := $b/autor return <autor>{$a}</autor> gibt alle Autoren zurück Geschachtelte for-Klauseln for $i in (2, 3), $j in (4, 5) return <res>{$i*$j}</res> Ausgabe: <res>8</res> <res>10</res> <res>12</res> <res>15</res> let - mit let kann man atomare Werte oder XML-Fragmente an eine Variable binden, z.B. let $buch := doc("buchliste.xml")/buchliste/buch[1]/titel let $zahl := 5, $folge := (1,2,3) Zuweisungsoperator: ":=" Variablennamen beginnen mit "$" let $i := (1 to 3) (: entspricht let $i := (1,2,3) :) return <tupel>{ $i }</tupel> Ausgabe: <tupel>123</tupel> where Die where-Klausel dient zur Selektion und Filterung der Ergebnismenge. order by Ein FLWOR-Ausdruck ohne 'order by'-Klausel gibt das Ergebnis in Dokumentenreihenfolge aus. Wird eine order by-Klausel verwendet, kann das Ergebnis nach einem oder mehreren Werten aufsteigend (ascending) bzw. absteigend (descending) geordnet werden. Beispiel: In der Datei gehaltsliste.xml werden die Namen, sowie das Gehalt des Personals gespeichert. Die Daten sollen tabellarisch nach Namen aufsteigend und nach Gehalt absteigend sortiert werden! <table><th>Name</th><th>Gehalt</th>{ for $i in doc("gehaltsliste.xml")//person order by $i/nachname ascending, $i/vorname ascending, $i/gehalt cast as xs:decimal descending return <tr><td>{ $i/nachname/text() }, { $i/vorname/text() }</td><td>{ $i/gehalt/text() } EUR</td></tr> }</table> Besitzt wie in diesem Fall die Quelldatei ("gehaltsliste.xml") kein XML-Schema, muss eine Typkonvertierung vorgenommen werden, falls numerisch sortiert werden soll! Liefert das Sortierkriterium keine eindeutige Reihenfolge, so ist die Anordnung der Duplikate implementierungsabhängig. Ein der order by-Klausel vorangestellender Zusatz stable erzwingt in diesem Fall die Einhaltung der Dokumentenreihenfolge. Existieren Eigenschaften nicht, nach denen sortiert wird, so kann mit dem Sortierungsmodifikator empty greatest bzw. empty least die Einordnung dieser Einträge explizit gesteuert werden. return Die return-Klausel konstruiert das Ergebnis. Verbundoperationen (Joins) ... durch Wertegleichheit Verbundprädikat in der where‐Klausel for $person in doc("personen.xml")//person where $person//name = 'Karl Schmidt' return $person Verbundprädikat im Pfadausdruck doc("personen.xml")//person[.//name = 'Karl Schmidt'] ... durch Verfolgung von Referenzen Dieses Beispiel funktioniert nicht unter Oracle oder Altova XMLSpy! Gegeben sind für dieses Beispiel folgende Dokumente: join‐ref.xml mit zugehörigem Schema join.xsd Folgender XQuery-Ausdruck würde als Ergebnis eine Sequenz von Büchern mit Titel und Autor wie rechts abgebildet liefern: for $titel in doc("1.xml")//titel let $autor := id($titel/@autor) return <buch>{$titel,$autor}</buch> <buch> <titel>Harry Potter und der Stein der Weisen</titel> <autor>J. K. Rowling</autor> </buch> <buch> <titel>Harry Potter und die Kammer des Schreckens</titel> <autor>J. K. Rowling</autor> </buch> <buch> <titel>Der Herr der Ringe</titel> <autor>J. R. R. Tolkien</autor> </buch> Da dies, wie erwähnt, leider noch nicht unterstützt wird, wird hier auf ein analoges Beispiel mittels XSLT verwiesen: <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="/"> <xsl:for-each select="//titel"> <buch> <titel> <xsl:value-of select="."/> </titel> <autor> <xsl:value-of select="id(@autor)"/> </autor> </buch> </xsl:for-each> </xsl:template> </xsl:stylesheet> Outer Join Ein Outer Join wird durch Schachtelung zweier FLWOR‐Ausdrücke ermöglicht. Beispiele Gegeben sind die Dokumente "personen.xml" mit einer eindeutigen Personennummer (pnr), dem Personennamen und der Stadt. Desweiteren gibt es eine Liste mit E‐Mailadressen ("personen‐email.xml") mit den Personennummern und E‐Mail‐Adressen. Es soll eine Liste mit Namen und E‐Mail‐Adressen ausgegeben werden, wobei es nicht zu jeder pnr in "personen.xml" auch eine pnr in "personen‐email.xml" gibt. Somit muss ein Outer‐Join erfolgen, der wie folgt aussieht: <res>{ for $person in doc("personen.xml")//person return <person>{ $person/*, ( for $person2 in doc("personen-email.xml")//person where $person/@pnr = $person2/@pnr return $person2/email ) }</person> }</res> <res> <person> <name>Karl Schmidt</name> <email>[email protected]</email> </person> <person> <name>Max Zander</name> <email>[email protected]</email> </person> <person> <name>Uwe König</name> </person> <person> <name>Maria Abel</name> <email>[email protected]</email> </person> <person> <name>Albert Brecht</name> </person> </res> Gruppierungen In XQuery lassen sich ohne großen Aufwand leicht Gruppierungen durchführen. Oft hat man eine Datenquelle, die nach einem Kriterium gruppiert werden soll. In diesem Fall wird mit Hilfe der distinct-values‐Funtkion eine Liste unterschiedlicher werte gebildet und dann in der äußeren return‐Klausel über die Daten iteriert. Beispiele Gegeben ist das Dokument "personen.xml" mit einer eindeutigen Personennummer (pnr), dem Personennamen und der Stadt. Die Namen der Personen sollen nach den Städten gruppiert werden. <res>{ for $stadt in distinct-values(doc("personen.xml")//stadt) return element { $stadt } { doc("personen.xml")//name[../stadt = $stadt] } }</res> (: alternativ mit FLWOR-Ausdruck <res>{ for $stadt in distinct-values(doc("personen.xml")//stadt) return element { $stadt } { for $person in doc("personen.xml")//person where $person/stadt = $stadt return $person/name } }</res> :) <res> <Leipzig> <name>Karl Schmidt</name> <name>Uwe König</name> </Leipzig> <München> <name>Max Zander</name> </München> <Köln> <name>Maria Abel</name> <name>Albert Brecht</name> </Köln> </res> If-then-else-Anweisung Hierbei wird die gewohnte if‐then‐else‐Notation verwendet: if ( Bedingung ) then Ausdruck_1 else Ausdruck_2 Das else in der If‐then‐else‐Anweisung muss vorhanden sein. Wird es nicht benötigt, übergibt man einfach eine leere Sequenz! Beispiele <res>{ for $zahl in (3,8,2,5) return if( $zahl mod 2 = 0 ) then <x>{ $zahl, " ist eine gerade Zahl." }</x> else <x>{ $zahl, " ist eine ungerade Zahl." }</x> }</res> <res> <x>3 <x>8 <x>2 <x>5 </res> ist ist ist ist eine eine eine eine ungerade Zahl.</x> gerade Zahl.</x> gerade Zahl.</x> ungerade Zahl.</x> Quantoren Um zu testen, ob eine Bedingung für einige Werte bzw. für alle Werte einer Sequenz zutrifft, werden quantifizierte Ausdrücke verwendet. Diese geben einen booleschen Wert zurück und werden folgendermaßen notiert: ("some"|"every") $var "in" Sequenz "satisfies" Bedingung Beispiele <res>{ for $zahlen in (<x><a> <b>2</b> <b>4</b> <b>6</b> </a><a> <b>1</b> <b>3</b> <b>5</b> </a><a> <b>1</b> <b>4</b> <b>6</b> </a></x>)//a where some $zahl in $zahlen/b satisfies $zahl mod 2 = 0 return $zahlen }</res> <res> <a> <b>2</b> <b>4</b> <b>6</b> </a> <a> <b>1</b> <b>4</b> <b>6</b> </a> </res> <res>{ for $zahlen in (<x><a> <b>2</b> <b>4</b> <b>6</b> </a><a> <b>1</b> <b>3</b> <b>5</b> </a><a> <b>1</b> <b>4</b> <b>6</b> </a></x>)//a where every $zahl in $zahlen/b satisfies $zahl mod 2 = 0 return $zahlen }</res> <res> <a> <b>2</b> <b>4</b> <b>6</b> </a> </res> Namensräume Zu einem Namensraum gehört ein Präfix und eine URI (Uniform Resource Identifier). Er wird folgendermaßen deklariert: declare namespace Präfix = URI; XQuery hat mehrere vordefinierte Namensräume, die somit nicht deklariert werden müssen! Diese Namensräume können bis auf den xmlNamensraum überschrieben und neu deklariert werden! • xml = http://www.w3.org/XML/1998/namespace • xs = http://www.w3.org/2001/XMLSchema • xsi = http://www.w3.org/2001/XMLSchema-instance • fn = http://www.w3.org/2005/xpath-functions • local = http://www.w3.org/2005/xquery-local-functions Vordefinierte Funktionen In diesem Tutorial wird auf den Namespace 'fn' bei vordefinierten Funktionen verzichtet, da dieser bei XPath‐Ausdrücken nicht angegeben wird! Auf dieser Seite soll jedoch gezeigt werden, wie die ausführliche Schreibweise der Funktionen ist! Zeichenfunktionen Beispiele: <string_functions> <concat>{ fn:concat("XQuery"," ","ist ","eine",()," umfangreiche ","Anfragesprache ","für ","XML.") }</concat> <string-join>{ fn:string-join(("XQuery","ist","eine","umfangreiche","Anfragesprache","für","XML.")," ") }</string-join> <string-length>{ fn:string-length("XQuery ist eine umfangreiche Anfragesprache für XML.") }</string-length> </string_functions> Ergebnis: <string_functions> <concat>XQuery ist eine umfangreiche Anfragesprache für XML.</concat> <string-join>XQuery ist eine umfangreiche Anfragesprache für XML.</string-join> <string-length>52</string-length> </string_functions> Benutzerdefinierte Funktionen XQuery bietet neben den vordefinierten Funktionen auch die Möglichkeit benutzerdefinierte Funktionen anzulegen. Diese können sowohl im gleichen Ausdruck als auch in einer seperaten Bibliothek (Modul) sein. Im Gegensatz zu vordefinierten Funktionen, muss bei benutzerdefinierten Funktionen ein Namensraum angegeben werden! declare function prefix:function_name($parameter as datatype) as returnDatatype { (: function code here ... :) }; Benutzerdefinierte Funktionen beginnen mit declare function und müssen ein Präfix besitzen. Lokale Funktionen haben das Präfix local. Die Datentypen entsprechen den Datentypen des XML‐Schemas. Der Funktionskörper befindet sich in den geschweiften Klammern. Funktionen können rekursiv sein. Wenn kein Rückgabetyp festgelegt wird, ist der Defaultrückgabetyp AnyType. Beispiele declare function local:nachfolger( $n as xs:integer ) as xs:integer { $n + 1 }; <nachfolger>Der Nachfolger von 4 ist { local:nachfolger(4) }</nachfolger> declare function local:fakul( $n as xs:integer ) as xs:integer { if ($n = 0) then 1 else $n * local:fakul($n - 1) }; <fakul>Fakultät von 5 ist { local:fakul(5) }</fakul> Benutzerdefinierte Funktionen können auch in externen Dateien deklariert werden, in sogenannten Modulen. Funktionen, die in Modulen stehen, besitzen ebenfalls einen Namensraum, der sich von den vordefinierten Namensräumen unterscheiden sollte. Zusätzlich zu Funktionen, kann man lokal und in Modulen auch Variablen deklarieren! math_module.xq xquery version "1.0" encoding "UTF-8"; module namespace math="http://www.xquery-tutorial.de/math"; declare function math:vorgaenger( $n as xs:integer ) as xs:integer { $n - 1 }; declare variable $math:x := 12; using_math_module.xq xquery version "1.0" encoding "UTF-8"; import module namespace math="http://www.xquery-tutorial.de/math" at "math_module.xq"; <results> <res>{ math:vorgaenger( 9 ) }</res> <res>{ math:vorgaenger( $math:x ) }</res> </results>