XQuery und statische Typisierung

XQuery ist in SQL Server eine statisch typisierte Sprache. Sie gibt bei der Abfragekompilierung einen Typfehler aus, wenn ein Ausdruck einen Wert zurückliefert, dessen Typ oder Kardinalität von einer bestimmten Funktion oder einem Operator nicht angenommen wird. Darüber hinaus kann eine Überprüfung des statischen Typs auch erkennen, ob ein Pfadausdruck eines typisierten XML-Dokuments falsch typisiert ist. Der XQuery-Compiler realisiert zuerst die Normalisierungsphase, in der die impliziten Vorgänge, wie die Atomisierung, hinzugefügt werden. Anschließend erfolgen die Inferenz und die Überprüfung des statischen Typs.

Inferenz des statischen Typs

Die Inferenz des statischen Typs ermittelt den Rückgabetyp eines Ausdrucks. Hierbei wird aus den statischen Typen der Eingabeparameter und aus der statischen Semantik des Vorgangs der statische Typ des Ergebnisses abgeleitet. Beispiel: Der statische Typ des Ausdrucks 1 + 2,3 wird wie folgt abgeleitet:

  • Der statische Typ von 1 ist xs:integer und der statische Typ von 2,3 ist xs:decimal. Auf der Grundlage der dynamischen Semantik konvertiert die statische Semantik des Vorgangs + die ganze Zahl in eine Dezimalzahl und liefert einen Dezimalwert zurück. Der abgeleitete statische Typ ist dann xs:decimal.

Für nicht typisierte XML-Instanzen gibt es spezielle Typen, mit deren Hilfe angegeben wird, dass die Daten nicht typisiert sind. Diese Information wird bei der Überprüfung des statischen Typs und zur Durchführung bestimmter impliziter Datentypkonvertierungen verwendet.

Bei typisierten Daten wird der Eingabetyp aus der XML-Schemaauflistung abgeleitet, die die XML-Datentypinstanz einschränkt. Wenn das Schema beispielsweise nur Elemente vom Typ xs:integer zulässt, dann sind die Ergebnisse eines Pfadausdrucks, der dieses Element verwendet, null oder mehr Elemente vom Typ xs:integer. Dies wird zurzeit durch einen Ausdruck wie element(age,xs:integer)* ausgedrückt, in dem das Sternchen (*) die Kardinalität des resultierenden Typs angibt. In diesem Beispiel führt der Ausdruck zu null oder mehr Elementen mit dem Namen "age" und dem Typ xs:integer. Weitere Kardinalitäten sind genau 1 und werden über den Typnamen alone, zero oder one ausgedrückt und mit einem Fragezeichen angegeben (?) - oder 1 oder mehr, was durch ein Pluszeichen (+) angegeben wird.

In manchen Fällen kann die Inferenz des statischen Typs ableiten, dass ein Ausdruck immer eine leere Zeichenfolge zurückliefert. Beispielweise, wenn ein Pfadausdruck eines typisierten XML-Datentyps innerhalb eines Elements <customer> (/customer/name) nach einem Element <name> sucht, das Schema aber innerhalb eines Elements <customer> (/customer/name) kein Element <name> zulässt. In diesem Fall ergibt die Inferenz des statischen Typs, dass das Ergebnis leer ist. Hiermit werden fehlerhafte Abfragen erkannt und als statische Fehler angezeigt, sofern der Ausdruck nicht () oder data( () ) war.

Die detaillierten Inferenzregeln werden in der formalen Semantik der XQuery-Spezifikation angegeben. Microsoft hat diese nur geringfügig für typisierte XML-Datentypinstanzen angepasst. Die wichtigste Änderung zum Standard besteht darin, dass der implizite Dokumentknoten den Typ der XML-Datentypinstanz kennt. Aus diesem Grund wird ein Pfadausdruck der Form /age auf der Grundlage dieser Information exakt typisiert.

Mit dem Verwenden von SQL Server Profiler können Sie sehen, welche statischen Typen im Rahmen einer Abfragekompilierung zurückgeliefert werden. Dazu muss die Ablaufverfolgung das XQuery Static Type-Ereignis in der TSQL-Ereigniskategorie aufweisen.

Überprüfung des statischen Typs

Die Überprüfung des statischen Typs stellt sicher, dass bei der Ausführung zur Laufzeit nur Werte des für den Vorgang passenden Typs empfangen werden. Da die Typen nicht zur Laufzeit geprüft werden müssen, können potenzielle Fehler zu einem frühen Zeitpunkt der Kompilierung festgestellt werden. Dies verbessert die Leistung. Die statische Typisierung setzt jedoch voraus, dass der Verfasser der Abfrage diese sorgfältiger formuliert.

Folgende Typen können verwendet werden:

  • Typen, die durch eine Funktion oder einen Vorgang explizit zugelassen werden.

  • Ein Untertyp eines explizit zugelassenen Typs.

Untertypen werden auf der Grundlage der Untertypisierungsregeln definiert, um Ableitungen durch Beschränkung oder Erweiterung des XML-Schemas zu verwenden. Ein Typ S ist beispielsweise ein Untertyp von T, wenn alle Werte, die den Typ S besitzen, auch Instanzen des Typs T sind.

Darüber hinaus sind alle Ganzzahlwerte auf der Grundlage der Typhierarchie des XML-Schemas auch Dezimalwerte. Nicht alle Dezimalwerte sind allerdings ganze Zahlen. Aus diesem Grund ist eine ganze Zahl ein Untertyp einer Dezimalzahl, aber nicht umgekehrt. Die Operation + lässt beispielsweise nur Werte bestimmter Typen zu, wie die der numerischen Typen xs:integer, xs:decimal, xs:float und xs:double. Wenn Werte anderer Typen, wie xs:string, übergeben werden, gibt die Operation einen Typfehler aus. Dies wird als strenge Typisierung bezeichnet. Werte anderer Typen, wie der Typ atomic, der untypisiertes XML kennzeichnet, können implizit in einen Wert eines Typs konvertiert werden, den die Operation zulässt. Dies wird als schwache Typisierung bezeichnet.

Wenn eine implizite Konvertierung notwendig ist, stellt eine anschließende Überprüfung des statischen Typs sicher, dass an eine Operation nur Werte des zugelassenen Typs mit der richtigen Kardinalität übergeben werden. Für "string" + 1 erkennt sie, dass der statische Typ von "string" xs:string ist. Da dieser Typ für die Operation + unzulässig ist, wird ein Typfehler ausgegeben.

Wenn das Ergebnis eines beliebigen Ausdrucks E1 zu einem beliebigen Ausdruck E2 addiert wird (E1 + E2), bestimmt die Inferenz des statischen Typs zuerst die statischen Typen von E1 und E2 und prüft diese dann gegen die für den Vorgang zulässigen statischen Typen. Wenn der statische Typ von E1 beispielsweise entweder xs:string oder xs:integer sein darf, ergibt die Überprüfung des statischen Typs auch dann einen Typfehler, wenn einige Werte zur Laufzeit ganze Zahlen sind. Dasselbe würde passieren, wenn der statische Typ von E1 xs:integer* wäre. Weil die Operation + nur genau einen Ganzzahlwert zulässt und E1 null oder mehr als 1 zurückliefern könnte, führt die Überprüfung des statischen Typs zu einem Fehler.

Wie zuvor erwähnt, ermittelt die Typinferenz häufig einen Typ, der breiter angelegt ist, als dies dem Benutzer hinsichtlich des zu übergebenden Datentyps bekannt ist. In diesen Fällen muss der Benutzer die Abfrage neu schreiben. Einige typische Fälle:

  • Der Typ folgert einen allgemeineren Typ, wie einen Untertyp oder eine Typvereinigung. Wenn der Typ atomic ist, müssen Sie den Datentypkonvertierungsausdruck oder die Konstruktorfunktion verwenden, um den tatsächlichen statischen Typ anzugeben. Wenn der abgeleitete Typ des Ausdrucks E1 beispielsweise eine Auswahl zwischen xs:string oder xs:integer ist und für die Addition xs:integer erforderlich ist, müssen Sie statt E1+E2xs:integer(E1) + E2 angeben. Dieser Ausdruck kann zur Laufzeit fehlschlagen, wenn ein Zeichenfolgenwert auftaucht, der nicht in xs:integer konvertiert werden kann. Der Ausdruck wird jetzt jedoch die Überprüfung des statischen Typs durchlaufen. Seit SQL Server 2005 wird dieser Ausdruck der leeren Sequenz zugeordnet.

  • Aus dem Typ wird eine höhere Kardinalität abgeleitet, als die tatsächlich in den Daten enthaltene. Dies passiert häufig, weil der xml-Datentyp mehr als ein Element der obersten Ebene enthalten kann, und weil eine XML-Schemaauflistung dies nicht erzwingen kann. Um den statischen Typ zu reduzieren und sicherzustellen, dass stattdessen maximal ein Wert übergeben wird, müssen Sie das Positionsprädikat [1] verwenden. Beispiel: Um 1 zum Wert des Attributs c des Elements b unter dem Element der obersten Ebene zu addieren, müssen Sie write (/a/b/@c)[1]+1 angeben. Zusätzlich können Sie mit einer XML-Schemaauflistung das Schlüsselwort DOCUMENT verwenden.

  • Bei einigen Vorgängen geht bei der Inferenz die Typinformation verloren. Beispiel: Wenn der Typ eines Knotens nicht bestimmt werden kann, wird er zu anyType. Dieser wird nicht implizit in irgendeinen anderen Typ umgewandelt. Das tritt am deutlichsten während der Navigation mithilfe der übergeordneten Achse auf. Sie sollten die Verwendung solcher Vorgänge vermeiden und die Abfrage neu schreiben, falls der Ausdruck einen statischen Typfehler erzeugt.

Typüberprüfung von Union-Typen

Union-Typen erfordern aufgrund der Typüberprüfung eine sorgfältige Handhabung. In den folgenden Beispielen sind zwei der Probleme dargestellt.

Beispiel: Funktion für Union-Typ

Stellen Sie sich eine Elementdefinition für <r> eines Union-Typs vor:

<xs:element name="r">
<xs:simpleType>
   <xs:union memberTypes="xs:int xs:float xs:double"/>
</xs:simpleType>
</xs:element>

Im XQuery-Kontext gibt die "Durchschnittsfunktion" fn:avg (//r) einen statischen Fehler zurück, weil der XQuery-Compiler keine Werte verschiedener Typen (xs:int, xs:float oder xs:double) für die <r>-Elemente im Argument von fn:avg() hinzufügen kann. Um dieses Problem zu beheben, müssen Sie den Funktionsaufruf als fn:avg(for $r in //r return $r cast as xs:double ?) umschreiben.

Beispiel: Operator für Union-Typ

Die Additionsoperation ('+') erfordert präzise Typen der Operanden. Als Ergebnis gibt der Ausdruck (//r)[1] + 1 einen statischen Fehler zurück, der die zuvor beschriebene Typdefinition für das Element <r> aufweist. Eine mögliche Lösung besteht im Umschreiben des Ausdrucks als as (//r)[1] cast as xs:int? +1, wobei das "?" das Auftreten von 0 oder 1 anzeigt. Seit SQL Server 2005 erfordert SQL Server "cast as" mit "?", weil jede Umwandlung die leere Sequenz als ein Ergebnis von Laufzeitfehlern verursachen kann.

Siehe auch

Andere Ressourcen