Man benötigt für die Navigation innerhalb eines XML-Dokuments eine möglichst
kurze Ausdruckssprache. Dies ist sowohl in XML Schema wie auch in XSLT / XSL-FO
oder eben im MS SQL Server die Navigations- und Abfragesprache XPath.
Man benötigt für die Navigation innerhalb eines XML-Dokuments eine möglichst kurze Ausdruckssprache. Dies ist sowohl in XML Schema wie auch in XSLT / XSL-FO oder eben im MS SQL Server die Navigations- und Abfragesprache XPath. Sie besitzt keine XML-Form, damit sie möglichst kurze Ausdrücke hervorbringt, welche allerdings bei steigenden Anforderungen auch sehr umfangreich werden können.
Die offiziellen Dokumente finden Sie unter XML Path Language (XPath), Version 1.0, W3C Recommendation 16 November 1999 (http://www.w3.org/TR/xpath) und XML Path Language (XPath) 2.0, W3C Candidate Recommendation 3, November 2005(http://www.w3.org/TR/xpath20/).

Ein XPath-Ausdruck findet sieben verschiedene Knotenarten, für die es jeweils eine eigene Syntax gibt:
Knoten wie Elemente, Attribute und Namensräume werden über so genannte Achsen angesteuert, die in Dokumentrichtung (westliche Leserichtung, von oben nach unten) oder entgegen gesetzt zur Dokumentrichtung (von unten nach oben) Knoten finden. Sie ähneln einem Familienstammbaum und decken den gesamten Baum ab, der von einem XML-Dokument aufgespannt wird.
Vorwärts |
Rückwärts |
| child (Kind) descendant (Nachfolger) attribute (Attribut) self (Ich) descendant-or-self (Nachfolger und ich) following-sibling (Folgende Geschwister) following (Folgende) namespace (Namensraum) |
parent (Eltern) ancestor (Vorfahren) preceding-sibling (Vorhergehende Geschwister) preceding (Vorhergehende) ancestor-or-self (Vorfahren und ich) |
Die nachfolgenden Abbildungen sollen zusammen mit den Beispielen die Verwendung der verschiedenen Achsen demonstrieren. Die Knoten werden noch ohne Prädikate, d.h. ohne Bedingungen ausgewählt. Als Beispieldokument verwenden sie das in diesem Kapitel erzeugte Dokument products2.xml.
Folgende Ausdrücke gelten vom Wurzelknoten:
Folgende Ausdrücke gelten vom Wurzelelement Product-List
Folgende Ausdrücke gelten von einem Product-Element:

Die einfache Auswahl von sämtlichen Knoten einer Achse ist nicht so interessant, wie die Verwendung von Prädikaten, welche quasi die WHERE-Klausel eines XPath-Ausdrucks angeben. Ein solcher setzt sich aus drei Komponenten zusammen, die als Einheit bzw. als Lokalisierungsschritt bezeichnet werden:
Wie in SQL sind auch die verschiedenen Operatoren für Vergleiche, Berechnungen und Logik verfügbar, wobei in XSLT wie in HTML die entsprechenden Entitäten zum Einsatz kommen müssen. Als Ergebnis erhält man SQL-ähnliche Abfragemöglichkeiten, die keine XML-Dokumente zurückliefern, sondern Ergebnismengen, die bspw. mit XSLT verarbeitet werden können oder die auch in SQL verwendet werden können.
Vergleich |
Bedeutung |
Kalkül |
Bedeutung |
Logik |
Bedeutung |
| = | Gleichheit | + | Addition | and | Und |
| != | Ungleichheit | - | Subtraktion | or | Oder |
| <, < | kleiner | * | Multiplikation | not | Nicht |
| >; > | größer | div | Division | ||
| <=, <= | kleiner gleich | mod | Modulo | ||
| >=, >= | größer gleich |
Folgende Beispiele gelten vom Wurzelknoten aus:
Schließlich gibt es noch eine Bibliothek an Funktionen, mit denen bspw. basale Rechen- und Zeichenkettenmanipulationen vorgenommen werden können. In der jeweiligen Version 2.0 gibt es ungleich mehr Funktionen, die hier allerdings keinen Platz finden können. Die Knotenmengen-Funktionen ermitteln allgemeine Informationen über Knoten oder erlauben den Zugriff auf Knoten über diese Informationen
Die Zeichenketten-Funktionen ermöglichen ebenfalls basale Manipulation von Zeichenketten für die Ausgabe in XSLT oder die Suche in XPath-Prädikaten.
Die logischen Funktionen liefern Wahrheitswerte oder kehren diese um.
Die numerischen Funktionen führen basale Rechenoperationen durch, wobei allerdings insbesondere das Runden nur mit Version 2.0 wie in anderen Sprachen mit Dezimalstellenangabe funktioniert.
Folgende Beispiele gehen vom Wurzelknoten aus:
Auf Basis der XPath-Syntax gibt es eine weitere Möglichkeit, XML-Daten zu befragen, wobei nicht nur die XPath-Ergebnismengen entstehen, sondern darüber hinaus ganze XML-Dokumente als Antwort erstellt werden können. In diesem Zusammenhang kann XQuery auch als teilweiser Ersatz für die Verarbeitung mit XSLT gesehen werden, zumal die Syn-tax wesentlich kompakter ist als die Erzeugung des gleichen Resultats mit XSLT. Während bei XPath 1.0 zwar ebenfalls ein großes theoretisches Fundament existiert, so ist es bei XPath 2.0 und XQuery 1.0 umso umfangreicher und aufschlussreicher. Dies kann in diesem Abschnitt aus Platzgründen nicht einmal ansatzweise dargestellt werden. Stattdessen sollen die für den Programmierer praktischen Bereiche aus dem Dokument XQuery 1.0: An XML Query Language, W3C Candidate Recommendation 3 November 2005 (http://www.w3.org/TR/xquery/) kurz dargestellt werden.
Das Akronym FLOWR soll sich „flower“ sprechen und greift die Schlüsselwörter for, let, where, order by und return auf, mit deren Hilfe SQL-ähnliche Abfragen erstellt werden können, die neben den Aspekten Auswahl, Einschränkung und Sortierung auch die Ausgabe in XML und damit eine Art der Umwandlung berühren.
Mit den beiden Schlüsselwörtern for und let wird jeweils eine Tupel-Sequenz von gebundenen Variablen erstellt, die Tupel-Strom (engl. tuple sequence) genannt wird. Sie lässt sich weiter einschränken, sortieren und schließlich in XML formatiert ausgeben.
Schlüsselwort |
Bedeutung |
| for | Enthält einen Ausdruck (Bindesequenz, engl. binding sequence), der einen Knotenssatz zurückliefert, über den mit Hilfe wenigstens einer gebundenen Variable iteriert werden kann. |
| let | Enthält wenigstens eine Variable, deren jeweiliger Wert ohne Iteration mit der Binde-Sequenz verbunden ist. |
| order by | Sortierung mit ascending (Standardwert) oder descending |
| where | Einschränkung unter Angabe eines XPath-Ausdrucks |
| return | Erstellung der Rückgabesequenz |
Das nächste Beispiel soll einen allgemeinen Eindruck von der XQuery-Syntax vermitteln. Der besondere Unterschied zwischen XPath und XQuery besteht darin, dass nicht nur eine Knotenmenge ausgewählt und diese dann zurückgeliefert wird, sondern dass dabei auch noch die Konstruktion einer Ergebnis-XML-Datei stattfindet, deren Strukturen ebenfalls im Rahmen der Abfrage angegeben werden können. In dieser Hinsicht konkurriert XQuery dann mit XSLT, welches ebenfalls in der Lage wäre, die Ausgaben der Beispiele in diesem Abschnitt zu erzeugen. Allerdings wäre die Syntax nicht so kompakt, weil XSLT ein XML-Dokument darstellt und XQuery dagegen nicht. Dies macht möglicherweise die Erarbeitung der Syntax für XSLT-Umsteiger schwieriger.
Im nächsten Beispiel erstellt man zunächst ein Products-Element als Wurzelelement für das Ergebnis. Innerhalb von for referenziert man dann die zu verarbeitenden Elemente, welche der Reihe nach abgerufen werden sollen. In diesem Fall sind es alle Product-Elemente. Mit Hilfe von let lassen sich Variablen erstellen, wobei in diesem Fall der je-weilige Produktname in einer Variable gespeichert wird. In where gibt man wie in SQL eine Bedingung an, wobei in diesem Fall dann überprüft wird, dass nur Produkte blauer Farbe abgerufen werden. Schließlich ist auch noch mit order by eine Sortierung wie in SQL möglich.
Neben der Angabe des Wurzelelements liefert return die Möglichkeit das gewünschte Ausgabeformat zu erstellen. Im einfachsten Fall kann man hier direkt XML-Vorgaben treffen. Dynamische Werte gibt man innerhalb von geschweiften Klammern an, wobei hier und auch an anderen Stellen innerhalb von XQuery wieder XPath-Ausdrücke sowie XPath-Funktionen zum Einsatz kommen.
xquery version "1.0";
<Products>
{
for $products in /Product-List/Product
let $product := $products/Name
where $products/Details/Color = 'Blue'
order by $products/Name
return
<Product P-Nr="{$product/parent::Product/@P-Nr}">
<Name >{$product/text()}</Name>
<Price>{$product/parent::Product/Prices/
List/text()}</Price>
</Product>
}
</Products>
Man erhält als Ergebnis eine XML-Datei (und nicht nur einen einfachen Wert oder eine bereits vorhandene Knotenmenge). Dies zeigt sehr schön, dass eine XQuery-Abfrage neben einer Abfrage auch eine Umwandlung bedeutet, denn das erzeugte Antwort-XML ist völlig anders aufgebaut als die ursprünglichen Daten.
Die nachfolgende Tabelle fasst die verschiedenen Konstruktoren zusammen. Dabei stehen in den allgemeinen Syntaxangaben die Abkürzungen QName für den qualifizierten XML-Namen mit Namensraumpräfix, NCName für den unqualifizierten Namen ohne Namensraumpräfix, ContentExpr für Inhalt(sausdruck) und Expr für einen beliebigen, sinnvollen (XPath-)Ausdruck. Insbesondere der Inhalt für Elemente kann sehr umfangreich sein, da hier ja auch Verschachtelungen angelegt werden können.
Konstruktor |
Syntax |
Beschreibung |
| element | element (QName | ({ Expr })) { ContentExpr? } | Erzeugt ein Element mit dem in QName angegeben oder aus dem Expr ermittelten Namen und optionalem Inhalt. |
| attribute | attribute (QName | ({ Expr })) { Expr? } | Erzeugt ein Attribut mit dem in QName angegeben oder aus dem Expr ermittelten Namen und optionalem Inhalt. |
| document | document { Expr } | Erzeugt ein XML-Wurzelelement und enthält den XML-Inhalt. |
| text | text { Expr } | Erzeugt einen Textknoten mit dem angegebenen Inhalt. |
| processing- instruction |
processing-instruction (NCName | ({ Expr })) { Expr? } | Erzeugt eine Prozessoran-weisung mit dem in NCName angegebenen oder aus dem Expr ermittelten Namen und den in Expr angegebenen Inhalt. |
| comment | comment { Expr } | Erzeugt einen Kommentarknoten mit dem angegebenen Inhalt. |
Die berechneten Konstruktoren können im nächsten Beispiel begutachtet werden. Das Dokument erstellt nach einem Kommentar und einer Prozessoranweisung, die eine XSLT-Datei referenziert schließlich das Wurzelelement, wobei in diesem fall nicht wie zuvor XML-Syntax zum Einsatz kommt, sondern ebenfalls ein berechneter Konstruktor. Die FLOWR-Syntax bleibt erhalten, sodass nur die Ausgabe innerhalb von return die neu vorgestellten Elemente zeigt. Inhaltlich stellt dieses Beispiel schon eine auch in XSLT relativ anspruchsvolle Ausgabe in Form einer Gruppierung dar. Diese gelingt über die nur in XPath und XQuery 2.0 vorhandene und sehr nützliche distinct-values()-Funktion. Sie ruft alle Farben ohne Duplikate ab, sodass hier Gruppen in XML gebildet werden können. Innerhalb eines Color-Elements sollen die Produkte dieser Farbe enthalten sein.
Ob die berechneten Konstruktoren die Lesbarkeit fördern, ist zunächst eine Geschmackssache. Sie erscheinen in ähnlicher Weise bspw. auch in der Kurzsyntax von RelaxNG, einer Konkurrenztechnologie zu XML Schema. Die Gruppierung wäre jedenfalls auch mit der zuvor verwendeten Syntax denkbar gewesen.
xquery version "1.0";
comment { "ProductSummary, 18.01.2002" },
processing-instruction xml-stylesheet { "type='text/xsl' href='transformation.xslt'" },
element Product-Summary {
for $colors in distinct-values(//Color)
order by $colors descending
return
element Color {
attribute Name { $colors } ,
element Product-List {
for $product in //Product[Details/Color = $colors ]
let $name := $product/Name/text(),
$price := $product/Prices/List/text()
return
element Product {
element Name { $name },
element Price { $price }
}
}
}
}
In XQuery lässt sich eine Fallunterscheidung nutzen, die auch in XPath 2.0 zum Einsatz kommt. Dort ist zusätzlich auch der Einsatz von for möglich. Die allgemeine Syntax lautet if ( Expr ) then ExprSingle else ExprSingle, an der bereits die grundlegende Struktur if-then-else gut zu erkennen ist.
Noch einmal werden die Produkte auf die Farben getestet, wobei dieses Mal alle Produkte der Reihe nach von oben nach unten verarbeitet werden. Es findet also keine Filterung oder Gruppierung statt, sondern jeweils eine Untersuchung, ob das Produkt blau ist oder nicht. Ist es blau, gibt es eine Ausgabe in Form eines Elements; im gegenteiligen Fall nur einen Kommentar.
xquery version "1.0";
<Blue-Products>
{
for $bp in //Product
return
if ($bp/Details/Color = 'Blue')
then
<BlueProduct>
{ $bp/Name/text() }
</BlueProduct>
else <!-- Product different color-->
}
</Blue-Products>
Die Ausgabe ist nicht sonderlich interessant, zeigt aber die Funktionsweise der Iteration und die erfolgreiche Verwendung der Fallunterscheidungen.
<Blue-Products>
<!-- Product different color-->
<BlueProduct>Touring-3000 Blue, 50</BlueProduct>
<BlueProduct>Touring-3000 Blue, 44</BlueProduct>
<!-- Product different color-->
<!-- Product different color-->
</Blue-Products>
Auf der einen Seite kann man sich sehr gut vorstellen, dass die Syntax von XPath und XQuery noch viel mehr leisten kann als hier vorgestellt wurde. Für den T-SQL-Programmierer, der mit XML beginnt und im MS SQL Server seine ersten Gehversuche macht, sollten die hier beschriebenen Techniken schon für viele Ziele ausreichen. Es ist auch nicht immer möglich, alle Syntax-Elemente sinnvoll innerhalb einer Datenbank anzu-wenden, da hier auch noch ganz andere Möglichkeiten und Alternativen in T-SQL (Filter, Zusammensetzen des XML über Zeichenketten etc.) genutzt werden können, die zu Lösungen in den umrissenen XML-Technologien einen durchaus gangbaren Umweg darstellen.
Lediglich eine Information und Einschränkung ist wichtig: Der MS SQL Server 2005 unterstützt XQuery und damit auch die neue Funktionsbibliothek. Es wurde in diesem Kapitel nur die Bibliothek von Version 1.0 vorgestellt, die für einfache Zwecke gute Dienste leistet, aber aufgrund ihrer Beschränkung nur in Umwegen auch eine schnelle und einfache Lösung liefert. Die Bibliothek von 2.0 ist allerdings so viel umfangreicher, dass eine Darstellung dieses ohnehin schon sehr lange Kapitel endgültig sprengen würde. Hier genügt allerdings auch ein Blick in die Dokumentation des W3C, wie sie oben angegeben wurde, welche eine einfache Auflistung von Funktionsnamen und Parametern enthält. Zeichenkettenverarbeitung, Duplikatausblendung, Zeitberechnungen sowie typische Aggregatfunktionen sind hier genauso wie in SQL versammelt und warten nur darauf, entdeckt zu werden.
Die Funktion sql:column() ermöglicht es, auf relationale Spalten aus XQuery-Ausdrücken heraus zuzugreifen. Damit kann man gleichzeitig in einer XQuery-Abfrage auf XML-Spalten/Variablen wie auch auf relationale Spalten zugreifen, um bspw. ein gemeinsames Dokument zu erstellen. In der nachfolgenden Abfrage erstellt man die schon zuvor benutzte XML-Variable mit den Produktinformationen. Die Anweisung ist hier noch einmal abgedruckt, da sie für die nächsten Beispiele gemeinsam gilt und schon längere Zeit nicht mehr erfolgt ist. Das Ergebnis dieser Abfrage zerlegt man dann relational, als würde man wieder direkt auf eine Tabelle wie Product zugreifen, um nun aber dennoch eine XQuery-Anweisung auf diese relationalen Spalten auszuführen. Dadurch lässt sich dann wiederum ein XML-Dokument als Ergebnis konstruieren.
-- XML-Variable erstellen
DECLARE @productXML xml, @idoc int
-- XML abrufen
SET @productXML = (
SELECT ProductNumber AS "P-Nr",
Name AS "Name",
...
FROM Production.Product
FOR XML PATH('Product'), ROOT('Product-List'))
-- Standardzerlegung für alle Zeilen
EXEC sp_xml_preparedocument @idoc OUTPUT, @productXML
SELECT @productXML.query('
element ProductInfo {
element ProductId { sql:column("ProductNumber") },
element ProductName { sql:column("Name") },
...
} ') as Result
FROM OPENXML(@idoc, '/Product-List/Product',2)
WITH (ProductNumber nvarchar(25) 'P-Nr',
Name nvarchar(50) 'Name',
...)
Man erhält als Ergebnis für jede aus der XML-Struktur relational umgewandelte Zeile wiederum ein XML-Dokument zurück.
<ProductInfo>
<ProductId>BK-R19B-48</ProductId>
<ProductName>Road-750 Black, 48</ProductName>
<ProductStandardCost>343.6496</ProductStandardCost>
<ProductListPrice>539.99</ProductListPrice>
<ProductSize>48</ProductSize>
<ProductColor>Black</ProductColor>
</ProductInfo>
Die Funktion sql:variable() bietet die Möglichkeit, auf vorab deklarierte T-SQL-Variablen zuzugreifen, wobei lediglich der Name der Variable inklusive @-Zeichen als Zeichenkette übergeben werden muss. Dies eignet sich dann für dynamische Filter bspw. in XPath-Ausdrücken. Das nachfolgende Beispiel zeigt den Standardfall, in dem innerhalb der where-Klausel eines XQuery-Ausdrucks ein Filter in XPath-Notation verwendet wird. Um nun aus der zuvor erstellten XML-Variable nur noch die schwarzen Produkte abzurufen, kann man innerhalb des XPath-Ausdrucks auf die zuvor erstellte Variable @Color zugreifen.
DECLARE @Color [varchar](20)
SET @Color='Black'
SELECT @productXML.query('<Product-List> {
for $A in /Product-List/Product
where $A [Details/Color = sql:variable("@Color")]
return $A}
</Product-List>')
Auch wenn die Funktionen von XPath 1.0 schon vorgestellt wurden, soll noch einmal kurz darauf hingewiesen werden, dass diese Funktionen und die Funktionen von XPath 2.0 und XQuery 2.0 ebenfalls im MS SQL Server genutzt werden können. Durch sie steht ein unglaublicher Sprachreichtum zur Verfügung, der neben Zeichenkettenbearbeitung, Berechnungen sowie XML-Bearbeitungen und –Manipulationen eigentlich immer eine Lösung bieten sollte. Für die sicherlich besonders häufig gebrauchten Aggregatfunktionen zeigt das nächste Beispiel, wie einfach diese Funktion in einer XQuery-Abfrage eingefügt werden können und diverse Berechnungen durchführen. Interessant ist insbesondere, wie einfach eine „Unterabfrage“, die hier nur nicht so heißen braucht, da die Korrelation gar nicht sichtbar ist, ausgeführt werden kann, um die Produkte mit dem kleinsten/größten Preis ausfindig zu machen. Es ist lediglich eine Aggregatfunktion auf der rechten Seite eines Filters notwendig, welche den benötigten Wert abruft.
SELECT @productXML.query('<Aggregate>
<Mittelwert>{avg(/Product-List/Product/Prices/Standard)
}</Mittelwert>
<Summe>{sum(/Product-List/Product/Prices/Standard)}</Summe>
<Anzahl>Produkte:{count(/Product-List/Product)}</Anzahl>
<Produkteliste-Minimum>{
for $products in /Product-List/Product
where $products/Prices/Standard = min(//Standard)
return $products }</Produkteliste-Minimum>
<Produktliste-Maximum>{
for $products in /Product-List/Product
where $products/Prices/Standard = max(//Standard)
return $products}</Produktliste-Maximum></Aggregate>
')
Man erhält ein relative langes XML-Dokument, da eine große Anzahl an Produkten einen Preis von 0 besitzen und daher diese Liste innerhalb der XML-Struktur sehr umfangreich ist.
<Aggregate>
<Mittelwert>258.602961309524</Mittelwert>
<Summe>130335.8925</Summe>
<Anzahl>Produkte:504</Anzahl>
<Produkteliste-Minimum>
<Product>
<P-Nr>RC-0291</P-Nr>
<Name>Rear Derailleur Cage</Name>
<Prices>
<Standard>0.0000</Standard>
<List>0.0000</List>
...
comelio.com
