Angeben von Prädikaten in einem Pfadausdrucksschritt

Wie im Thema Pfadausdrücke in XQuery beschrieben wurde, umfasst ein Achsenschritt in einem Pfadausdruck die folgenden Komponenten:

Das optionale Prädikat ist der dritte Teil des Achsenschritts in einem Pfadausdruck.

Prädikate

Ein Prädikat wird zum Filtern einer Knotensequenz durch Anwenden eines angegebenen Tests verwendet. Der Prädikatausdruck ist in eckige Klammern eingeschlossen und an den letzten Knoten in einem Pfadausdruck gebunden.

Angenommen, es wird z. B. ein SQL-Parameterwert (x) des xml-Datentyps deklariert, wie das im Folgenden gezeigt wird:

declare @x xml
set @x = '
<People>
  <Person>
    <Name>John</Name>
    <Age>24</Age>
  </Person>
  <Person>
    <Name>Goofy</Name>
    <Age>54</Age>
  </Person>
  <Person>
    <Name>Daffy</Name>
    <Age>30</Age>
  </Person>
</People>
'

In diesem Fall sind die folgenden Ausdrücke gültig, die einen Prädikatwert von [1] für jede der drei unterschiedlichen Knotenebenen verwenden:

select @x.query('/People/Person/Name[1]')
select @x.query('/People/Person[1]/Name')
select @x.query('/People[1]/Person/Name')

Beachten Sie, dass in jedem Fall das Prädikat an den Knoten im Pfadausdruck gebunden wird, für den es angewendet wird. Beispielsweise wählt der erste Pfadausdruck das erste <Name>-Element innerhalb jedes /People/Person-Knotens aus und gibt mit der bereitgestellten XML-Instanz folgendes Ergebnis zurück:

<Name>John</Name><Name>Goofy</Name><Name>Daffy</Name>

Dagegen wählt der zweite Pfadausdruck alle <Name>-Elemente aus, die sich unter dem ersten /People/Person-Knoten befinden. Deshalb gibt er das folgende Ergebnis zurück:

<Name>John</Name>

Es können auch Klammern verwendet werden, um die Auswertungsreihenfolge des Prädikats zu ändern. So wird z. B. im folgenden Ausdruck ein Klammernsatz verwendet, um den Pfad von (/People/Person/Name) vom Prädikat [1] zu trennen:

select @x.query('(/People/Person/Name)[1]')

In diesem Beispiel ändert sich die Reihenfolge, in der das Prädikat angewendet wird. Das liegt daran, dass der in Klammern eingeschlossene Pfad (/People/Person/Name) zuerst ausgewertet und anschließend der Prädikatoperator [1] für das Set angewendet wird, das alle mit dem eingeschlossenen Pfad übereinstimmenden Knoten enthält. Ohne die Klammern wäre die Reihenfolge des Vorgangs anders: Der Prädikatoperator [1] würde dann als ein child::Name-Knotentest angewendet werden, wie das im ersten Beispiel für den Pfadausdruck der Fall war.

Quantifizierer und Prädikate

Quantifizierer können innerhalb der Klammern des Prädikats mehrfach verwendet und hinzugefügt werden. So wäre z. B. unter Nutzung des vorigen Beispiels folgende Verwendung von mehreren Quantifizierern innerhalb eines komplexen Prädikatunterausdrucks gültig.

select @x.query('/People/Person[contains(Name[1], "J") and xs:integer(Age[1]) < 40]/Name/text()')

Das Ergebnis eines Prädikatausdrucks wird in einen booleschen Wert konvertiert und wird als Wahrheitswert des Prädikats bezeichnet. Im Ergebnis werden nur die Knoten in der Sequenz zurückgegeben, deren Prädikatwahrheitswert True ist. Alle anderen Knoten werden verworfen.

Beim folgenden Pfadausdruck ist z. B. ein Prädikat in den zweiten Schritt eingeschlossen:

/child::root/child::Location[attribute::LocationID=10]

Die durch dieses Prädikat angegebene Bedingung wird auf alle untergeordneten Elemente des <Location>-Elementknotens angewendet. Das führt dazu, dass als Ergebnis nur solche Arbeitsplatzstandorte zurückgegeben werden, bei denen der Attributwert der Standortkennung (LocationID) 10 ist.

Der vorige Pfadausdruck wird in der folgenden SELECT-Anweisung ausgeführt:

SELECT Instructions.query('
declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
 /child::AWMI:root/child::AWMI:Location[attribute::LocationID=10]
')
FROM Production.ProductModel
WHERE ProductModelID=7

Berechnen des Prädikatwahrheitswerts

Gemäß den XQuery-Spezifikationen werden die folgenden Regeln angewendet, um den Prädikatwahrheitswert zu ermitteln:

  1. Wenn der Wert des Prädikatausdrucks eine leere Sequenz ist, ist der Prädikatwahrheitswert False.

    Beispiel:

    SELECT Instructions.query('
    declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
     /child::AWMI:root/child::AWMI:Location[attribute::LotSize]
    ')
    FROM Production.ProductModel
    WHERE ProductModelID=7
    

    Der Pfadausdruck in dieser Abfrage gibt nur solche <Location>-Elementknoten zurück, für die ein LotSize-Attribut angegeben ist. Wenn das Prädikat eine leere Sequenz für eine bestimmte <Location> zurückgibt, wird dieser Arbeitsplatzstandort nicht im Ergebnis zurückgegeben.

  2. Prädikatwerte können nur xs:integer, xs:Boolean oder node* sein. Bei node* wird das Prädikat als True ausgewertet, wenn es irgendwelche Knoten gibt, und als False für eine leere Sequenz. Alle anderen numerischen Typen wie z. B. double und float generieren einen statischen Typisierungsfehler. Der Prädikatwahrheitswert eines Ausdrucks ist ausschließlich dann True, wenn die resultierende ganze Zahl gleich dem Wert der Kontextposition ist. Außerdem führen nur Integer-Literalwerte und die last()-Funktion zur Verringerung der Kardinalität des gefilterten Schrittausdrucks auf 1.

    So führt z. B. die folgende Abfrage zum Abrufen des dritten untergeordneten Elementknotens des <Features>-Elements.

    SELECT CatalogDescription.query('
    declare namespace PD="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
    declare namespace wm="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelWarrAndMain";
     /child::PD:ProductDescription/child::PD:Features/child::*[3]
    ')
    FROM Production.ProductModel
    WHERE ProductModelID=19
    

    Beachten Sie hinsichtlich der vorherigen Abfrage Folgendes:

    • Der dritte Schritt im Ausdruck gibt einen Prädikatausdruck an, dessen Wert 3 ist. Deshalb ist der Prädikatwahrheitswert dieses Ausdrucks nur für solche Knoten True, deren Kontextposition 3 ist.

    • Der dritte Schritt gibt auch ein Platzhalterzeichen (*) an, das alle Knoten im Knotentest anzeigt. Das Prädikat filtert jedoch die Knoten und gibt nur den Knoten in der dritten Position zurück.

    • Die Abfrage gibt den dritten untergeordneten Elementknoten der untergeordneten Elemente des <Features>-Elements innerhalb der untergeordneten Elemente des <ProductDescription>-Elements des Stammelements zurück.

  3. Wenn der Wert des Prädikatausdrucks ein einfacher Werttyp des booleschen Datentyps ist, entspricht der Prädikatwahrheitswert dem Wert des Prädikatausdrucks.

    Die folgende Abfrage wird z. B. für eine Variable des xml-Typs angegeben, die eine XML-Instanz enthält, nämlich die XML-Instanz zur Kundenumfrage. Die Abfrage ruft solche Kunden ab, die über untergeordnete Elemente verfügen. In dieser Abfrage wäre das <HasChildren>1</HasChildren>.

    declare @x xml
    set @x='
    <Survey>
      <Customer CustomerID="1" >
      <Age>27</Age>
      <Income>20000</Income>
      <HasChildren>1</HasChildren>
      </Customer>
      <Customer CustomerID="2" >
      <Age>27</Age>
      <Income>20000</Income>
      <HasChildren>0</HasChildren>
      </Customer>
    </Survey>
    '
    declare @y xml
    set @y = @x.query('
      for $c in /child::Survey/child::Customer[( child::HasChildren[1] cast as xs:boolean ? )]
      return 
          <CustomerWithChildren>
              { $c/attribute::CustomerID }
          </CustomerWithChildren>
    ')
    select @y
    

    Beachten Sie hinsichtlich der vorherigen Abfrage Folgendes:

    • Der Ausdruck in der for-Schleife umfasst zwei Schritte, und der zweite Schritt gibt ein Prädikat an. Der Wert dieses Prädikats ist ein Wert des booleschen Datentyps. Wenn dieser Wert True ist, so ist auch der Wahrheitswert des Prädikats True.

    • Die Abfrage gibt die untergeordneten Elemente des <Customer>-Elemente aus den untergeordneten <Survey>-Elementen des Stammelements zurück, deren Prädikatwert True ist. Dies ist das Ergebnis:

      <CustomerWithChildren CustomerID="1"/> 
      
  4. Wenn der Wert des Prädikatausdrucks eine Sequenz ist, die mindestens einen Knoten enthält, so ist der Wahrheitswert des Prädikats True.

Die folgende Abfrage ruft z. B. aus dem Namespace, der dem wm-Präfix zugeordnet ist, die ProductModelID für Produktmodelle ab, deren XML-Katalogbeschreibung mindestens ein Merkmal enthält, das ein untergeordnetes Element des <Features>-Elements ist.

SELECT ProductModelID
FROM   Production.ProductModel
WHERE CatalogDescription.exist('
             declare namespace PD="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
             declare namespace wm="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelWarrAndMain";
             /child::PD:ProductDescription/child::PD:Features[wm:*]
             ') = 1

Statische Typisierung und Prädikatfilter

Die Prädikate können sich auch auf den statisch abgeleiteten Typ eines Ausdrucks auswirken. Integer-Literalwerte und die last()-Funktion verringern die Kardinalität des gefilterten Schrittausdrucks zumeist auf 1.