XPath

Comelio-Blog MS SQL Server XPathMan 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.

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:

  • Wurzelknoten (engl. root nodes)
  • Elementknoten (engl. element nodes)
  • Textknoten (engl. text nodes)
  • Attributknoten (engl. attribute nodes)
  • Namensraumknoten (engl. namespace nodes)
  • Prozessoranweisungen (engl. processing instruction nodes)
  • Kommentare (engl. comment nodes)

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:

  • Rufe alle Product-Elemente ab. Hier kann man die Kurzform //Product verwenden, die sämtliche Product-Elemente überall im Dokument findet. Die Form /Product-List/Product lässt sich auch einsetzen, um nur Product-Elemente innerhalb von Product-List zu finden. Eine Langform mit ausgeschriebenem Achsennamen (bei child nicht notwendig): /child::Product-List/child::Product
  • Zeige die Kinder von Prices: //Product/Prices/* oder auch in verlängerter Form //Product/Prices/child::*. Zeige die Kinder von Prices und Details: //Prices | //Details, wobei hier ein ODER zum Einsatz kommt, was zur gleichzeitigen Auswahl führt.
  • Zeige das Attribut P-Nr: //Product/@P-Nr. Zeige alle Attribute von Product: //Product/@*.

Folgende Ausdrücke gelten vom Wurzelelement Product-List

  • Zeige die Kinder bzw. die Product-Elemente an. Dies lässt sich über jeden einzelnen Teilausdruck von * | Product | child::* | child::Product erreichen.
  • Zeige alle Product-Elemente und ihre Kinder und Kindeskinder in Dokumentrichtung an: descendant::*.

Folgende Ausdrücke gelten von einem Product-Element:

  • Zeige das Name-Element an: Name oder child::Name.
  • Zeige das Color-Kind an, das innerhalb von Details liegt. Dies lässt sich mit descendant::Color oder mit Details/Color erreichen.
  • Wähle sich selbst aus: . . Wähle das eigene Attribut: @P-Nr. Wähle den Eltern-Knoten: parent::* oder mit ausgeschriebenem Namen: parent::Product-List. Wähle alle Vorfahren aus: ancestor::*. Wähle unter den Vorfahren nur das Wurzelelement: ancestor::Product-List.
  • Wähle die nachfolgenden Geschwister: following-sibling::Product. Wähle die vorherigen Geschwister: preceding-sibling::Product. Wähle die Size-Elemente der nachfolgenden Geschwister aus: following-sibling::Product/Details/Size.

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:

  1. Eine Achse, welche die Beziehung zwischen dem gerade ausgewählten und verarbeiteten Kontextknoten und den Knoten ausdrückt, die über den Lokalisierungspfad auszuwählen sind.
  2. Einen Knotentest, welcher den Knotentyp und den expandierten Namen der Knoten angibt, die durch den Lokalisierungspfad auszuwählen sind.
  3. Kein oder mehrere Prädikate, welche beliebige Ausdrücke verwenden, um genauere Eigenschaften anzugeben, die für die Knoten zutreffen sollen, welche durch den Lokalisierungspfad auszuwählen sind.

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
<, &lt; kleiner * Multiplikation not Nicht
>; &gt; größer div Division    
<=, &lt;= kleiner gleich mod Modulo    
>=, &gt;= größer gleich        

Folgende Beispiele gelten vom Wurzelknoten aus:

  • Wähle das Product-Element mit der angegebenen Nummer im P-Nr-Attribut aus: //Product[@P-Nr = 'BK-R19B-52']
  • Wähle alle Produkte mit einem Listenpreis größer 100 aus: //Product[Prices/List > '100'] oder //Product[descendant::List > '100'].
  • Wähle alle Produkte mit Listenpreis größer 100 und Größe 52 aus: //Product[descendant::List > '100' and descendant::Size = '52']
  • Wähle die Color-Elemente mit Wert Black und von ihnen das Großeltern-Element Product aus: //Product/Details[Color = 'Black']/ancestor::Product

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

  • position() ermittelt die aktuelle Position.
  • last() liefert den letzten Knoten einer Knotenmenge.
  • count(Knotenmenge) zählt die Knoten in einer Knotenmenge.
  • name(Knotenmenge) ermöglicht die direkte Auswahl von Knoten anhand ihres Namens mit Namensraumpräfix.
  • local-name(Knotenmenge) ermöglicht die direkte Auswahl von Knoten anhand ihres Namens ohne Namensraumpräfix.
  • namespace-uri(Knotenmenge) ermittelt den URI-Wert für einen Namensraum.
  • id(Objekt) ermittelt die Knotenmengen mit der angegebenen ID

Die Zeichenketten-Funktionen ermöglichen ebenfalls basale Manipulation von Zeichenketten für die Ausgabe in XSLT oder die Suche in XPath-Prädikaten.

  • starts-with(zk1, zk2) liefert true, wenn Zeichenkette 1 mit der Zeichenkette 2 beginnt.
  • contains(zk1, zk2) liefert liefert true, wenn Zeichenkette 1 in Zeichenkette 2 auftritt.
  • substring-before(zk1, zk2) ermittelt die Zeichen einer Zeichenkette 1 vor der Position, die in Zeichenkette 2 angegeben wurde: substring-before("12345", "3") liefert 12.
  • substring-after(zk1, zk2) ermittelt die Zeichen einer Zeichenkette 1 nach der Position, die in Zeichenkette 2 angegeben wurde: substring-after("12345", "3") also die Zahl 45.
  • substring(zk1, zk2, länge) ermittelt die Zeichen einer Zeichenkette 1 vor der Position, die in Zeichenkette 2 angegeben wurde, für die angegebene Länge: substring("123456789", "3", "2") liefert 34.
  • string-length(zk) ermittelt die Länge einer Zeichenkette.
  • string(Objekt) transformiert ein Objekt in eine Zeichenkette.
  • concat(zk1, zk2, ...) verknüpft die eingehenden Zeichenketten.

Die logischen Funktionen liefern Wahrheitswerte oder kehren diese um.

  • true() liefert den Wert wahr.
  • false() liefert den Wert falsch.
  • not(Objekt) liefert den Wert wahr, wenn das Objekt falsch ist.
  • boolean(Objekt) transformiert ein Objekt in einen logischen Wert, wobei nicht leere Knotenmengen und Zeichenketten sowie Zahlen größer 0 den Wert wahr ergeben.
  • lang(zk) liefert den Wert wahr, wenn der xml:lang-Wert (Standardattribut für ISO-Sprachkürzel wie de, en, fr usw.) mit der in der Zeichenkette angegebenen Sprache übereinstimmt.

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.

  • number(Objekt) transformiert ein Objekt in eine Zahl.
  • sum(Knotenmenge) berechnet die Summe der zu Zahlen transformierten Werte der Knotenmenge.
  • floor(Zahl) liefert die vorher liegende Ganzzahl.
  • ceiling(Zahl) liefert die nächstgrößere Ganzzahl.
  • round(Zahl) liefert den gerundeten Wert zur nächsten Ganzzahl.

Folgende Beispiele gehen vom Wurzelknoten aus:

  • Wähle das erste Product-Element: //Product[1], alle Product-Element nach dem vierten: //Product[position() > 4], das letzte Product-Element: //Product[position() = last()] aus.
  • Wieviele Product-Element mit Größe 52 gibt es? count(//Product[descendant::Size = '52'])
  • Summiere die Preise der schwarzen Produkte: sum(//Product[descendant::Color = 'Black']/Prices/List/text()). Leider muss man zusätzlich noch text() hinzufügen, um den nicht als Dezimalzahl erkannten Preis auszuwählen, da er vier statt zwei Stellen aufweist.
  • Welche Product-Elemente besitzen das Wort Road im Namen? Zeige diesen Namen an: //Product[substring-before(Name, 'Road')]/Name oder //Product[contains(Name, 'Road')]/Name. Welche Produkte beginnen mit Road und enthalten ihn nicht nur: //Product[substring(Name, 1, 4) = 'Road'].
  • Welche Summe ergibt sich, wenn man die Differenz zwischen Kosten und den Listenpreis rechnet: sum(//Product/descendant::List/text()) - sum(//Product/descendant::Standard/text()).

XQuery

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.

Grundsyntax

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 }
}
}
}
}

Fallunterscheidungen

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.

Relationale Spalten und Variablen adressieren

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>')

Aggregatfunktionen verwenden

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>
...

»Kontaktformular










comelio.com

  • Comelio GmbH
    Berlin
    Fon: +49(0)30-8145622-00
    Fax: +49(0)30-8145622-10
  • Comelio GmbH
    München
    Fon: +49(0)89-38156860-0
    Fax: +49(0)89-38156860-9
  • Comelio GmbH
    Hamburg
    Fon: +49(0)40-20934996-0
    Fax: +49(0)40-20934996-9
  • Comelio GmbH
    Wien
    Fon: +43-720-2097-97
    Fax: +43-720-2097-98