• +49-(0)721-402485-12
Ihre Experten für XML, XQuery und XML-Datenbanken

Deklaration benutzerdefinierter Funktionen

Der Auszug aus der Grammatik zur Deklaration einer benutzerdefinierten Funktion in XQuery ergibt sich wie folgt:

FunctionDecl::=declare function QName (
ParamList? ( ) | ( ) as SequenceType)
(EnclosedExpr | external)
ParamList::=Param(, Param )*
Param::=$ VarName TypeDeclaration?
TypeDeclaration::=as SequenceType

Der Bezeichner einer benutzerdefinierten Funktion muss ein QName mit einem nicht leeren Präfix zur Angabe eines Namensraumes sein. Da in der aktuellen Version des XQuery-Standards im Bereich der benutzerdefinierten Funktionen kein Überladen von Funktionen erlaubt ist, muss der Bezeichner einer Funktion innerhalb des Moduls eindeutig sein. Darüber hinaus ist es nicht erlaubt, eine Funktion mit dem gleichen Namen wie ein bereits existierender Typ zu deklarieren, da für jeden atomaren Datentyp grundsätzlich eine Konstruktorfunktion deklariert wird.

Die Forderung nach einer Namensraumangabe würde im Prinzip die Einführung eines neuen Namensraumes auch für lokal verwendete Funktionen innerhalb des aktuellen Moduls implizieren. Um dies zu vermeiden, führt XQuery einen impliziten Namensraum (www.w3.org/2003/11/xquery-local-functions) mit dem Präfix local ein, der bei der Deklaration benutzerdefinierter Funktionen für den lokalen Gebrauch verwendet werden kann.

Der Einsatz benutzerdefinierter Funktionen wird an folgendem Beispiel illustriert:

declare function local:BetreutePatienten(
$a as element(Arzt_T))
as element(Patient)*
{
fn:distinct-values(
for $p in fn:collection("Patienten")//Patient_stationär
where xqb:follow-xlink($p/Einlieferung/Arzt/@xlink:href) is $a
return $p )
};
for $a in fn:doc("Hochwaldklinik.xml")//Angestellte/Arzt,
$p in local:BetreutePatienten($a)
return
<Patientenliste>
<Patient>
<Patientname>{ $p/Name/* }</Patientname>
<Arztname>{ $a/Name/* }</Arztname>
</Patient>
</Patientenliste>

Dabei werden innerhalb der Funktion für einen konkreten Arzt alle vom ihm aufgenommenen Patienten ermittelt. Der Parameter ist somit vom TypArzt_T; der Rückgabewert der Funktion ist vom Sequenztyp Patient.

Im folgenden Beispiel wird das Gehalt der Angestellten berechnet, wobei in Abhängigkeit von der Zugehörigkeit zur Berufsgruppe die Überstunden unterschiedlich entlohnt werden:

declare function local:Gehaltsberechnung(
$a as element(Angestellter_T),
$h as xs:integer)
as xs:decimal
{
typeswitch ($a)
case element(*, Arzt_T)
return ($h + local:AnzahlÜberstunden($h)*0.5) * 25
case element(*, Pfleger_T)
return ($h + local:AnzahlÜberstunden($h)*0.4) * 15
case element(*, Techniker_T)
return ($h + local:AnzahlÜberstunden($h)*0.2) * 20
case element(*, Sekretärin_T)
return ($h + local:AnzahlÜberstunden($h)*0.09) * 10
default return 0
};
declare function local:AnzahlÜberstunden(
$h as xs:integer)
as xs:integer
{
if ($h > 38.5) then $h – 38.5
else 0
};

In diesem Beispiel werden zwei benutzerdefinierte Funktionen eingesetzt, um das Gehalt einschließlich der Zulagen bei Überstunden zu ermitteln. Die Funktion local:Gehaltsberechnung() nimmt eine Fallunterscheidung vor und berechnet mit dem jeweils gültigen Stundensatz multipliziert mit der Anzahl geleisteter Stunden das Gehalt pro Woche. Der Anteil der Überstunden, welcher mit der Funktion local:AnzahlÜberstunden() durch einen konditionalen Ausdruck ermittelt wird, wird dabei mit einem zusätzlichen Faktor belegt. Zum Beispiel erhält ein Arzt pro Überstunde neben seinem regulären Stundensatz zusätzlich 50% als Zuschlag. Eine Gehaltsliste für die Klinik wird dann wie folgt erstellt:

<Gehaltsliste>
{
for $a in fn:doc("Hochwaldklinik.xml")
//element(*, Angestellte_T)
for $z in fn:doc("Zeiterfassung.xml")
//Wochenstunden[@Kalenderwoche="48"]/Angestellter
where $a/Nummer = $z/Nummer
return
<Angestellter>
<Name>{ $a//Vorname, $a//Nachname }</Name>
<Gehalt>{ local:Gehaltsberechnung($a, $z/Stunden) }</Gehalt>
</Angestellter>
}
</Gehaltsliste>

Als weiteres Beispiel wird in [W3C-19] argumentiert, dass die Existenz von Standardwerten bei der Auswertung von Ausdrücken ein häufiges Szenario reflektiert. Die folgende benutzerdefinierte Funktion local:if-absent() akzeptiert eine Sequenz von Knoten und einen Standardwert vom Typ xdt:anyAtomicType.

declare function local:if-absent(
$node as node?,
$value as xdt:anyAtomicType) as xdt:anyAtomicType*
{
if ($node)
then fn:data($node)
else $value
}

Der konditionale Ausdruck überprüft dabei auf die Existenz eines Elementes innerhalb der Sequenz und liefert im positiven Fall die Sequenz selbst, im negativen Falls den als zweiten Parameter übergebenen Wert. Der konditionale Ausdruck kann durch

if ($node and $node/child::fn:node())

dahingehend erweitert werden, dass der Standardwert zurückgeliefert wird, wenn die Sequenz entweder leer ist oder ein Element mit einem komplexen Inhalt besitzt.

 

Quelle: "XQuery – Grundlagen und fortgeschrittene Methoden", dpunkt-Verlag, Heidelberg (2004)

<< zurückvor >>