XQuery / XQuery 3.0: Gruppierungen mit group by
![]() |
![]() |
➪ In XQuery 3.0 sind mit group by auch Gruppierungen möglich.
Analog zu xsl:for-each-group können Sie auch in XQuery einen Gruppierungsschlüssel individuell zusammenstellen.
xquery version "3.0";
<ergebnis>
{
for $m in //Mensch
let $gehalt := xs:decimal($m/Gehalt/text())
let $status := if ($gehalt > 5000 )
then ('reich')
else if ($gehalt <= 5000 and $gehalt > 1000 )
then ('wohlhabend')
else ('normal')
group by $status
return
<Kunde status="{$status}">
{
for $kk in $m
order by $kk/Gehalt/text() descending
return
<k NN="{$kk/name/text()}"
VN="{$kk/vorname/text()}"
EK="{$kk/Gehalt/text()}"
WO="{$kk/../name/text()}"/>
}
</Kunde>
}
</ergebnis>
Das Ergebnis können Sie hier sehen:
<ergebnis>
<Kunde status="normal">
<k NN="Sorglos" VN="Siggi" EK="987.58" WO="Neustadt"/>
<k NN="Sinnlos" VN="Simone" EK="876.54" WO="Darmstadt"/>
<k NN="Wertlos" VN="Werner" EK="777.77" WO="Darmstadt"/>
<k NN="Herzlos" VN="Heini" EK="654.21" WO="Neustadt"/>
<k NN="Hirnlos" VN="Horst" EK="546.77" WO="Darmstadt"/>
<k NN="Kolos" VN="Karl" EK="456" WO="Darmstadt"/>
<k NN="Rielos" VN="Lotte" EK="456" WO="Kapstadt"/>
<k NN="Lustlos" VN="Ludwig" EK="357" WO="Darmstadt"/>
<k NN="Rhodos" VN="Rudi" EK="333.33" WO="Darmstadt"/>
<k NN="Sagblos" VN="Stefan" EK="321.45" WO="Neustadt"/>
<k NN="Schlaflos" VN="Susi" EK="321" WO="Kapstadt"/>
<k NN="Holzflos" VN="Hugo" EK="234.56" WO="Neustadt"/>
<k NN="Ruhelos" VN="Rita" EK="234" WO="Kapstadt"/>
<k NN="Muehelos" VN="Martin" EK="222" WO="Kapstadt"/>
<k NN="Leinenlos" VN="Liane" EK="135" WO="Kapstadt"/>
</Kunde>
<Kunde status="wohlhabend">
<k NN="Bodenlos" VN="Betty" EK="3450" WO="Kapstadt"/>
<k NN="Nixlos" VN="Nicole" EK="1234.56" WO="Neustadt"/>
</Kunde>
<Kunde status="reich">
<k NN="Wasistlos" VN="Willi" EK="6789" WO="Kapstadt"/>
<k NN="Sprachlos" VN="Stefan" EK="5430" WO="Neustadt"/>
</Kunde>
</ergebnis
Das Type-Casting auf xs:decimal zur exakten Zahlenberechnung lohnt sich auch in XQuery 3.0, wie das folgende Beispiel zeigt.
xquery version "3.0";
<ergebnis> {
for $k in //Kauf
let $artikel := $k/bez/text()
group by $artikel
return
<artikel
name="{$artikel}">
{
for $kk in $k
return
<einkauf
anzahl="{$kk/anzahl}"
einzelpreis="{$kk/preis}"
gesamt="{$kk/Gesamt}"
gesamt2="{$kk/anzahl * $kk/preis}"
gesamt3="{
xs:decimal($kk/anzahl)
* xs:decimal($kk/preis)
}"/>
}
</artikel>
}</ergebnis>
Das Ergebnis haben Sie zur besseren Übersicht auf einige wenige Resultate verkürzt. Das Gesamt-Attribut entstammt der Datenquelle, deren ursprünglicher Wert per Datenbankabfrage ermittelt wurde ("SELECT anzahl*preis as Gesamt ..."). Das gesamt2-Attribut arbeitet offenbar mit einem internen Double-Type-Cast, kommt aber zu einem anderen Ergebnis als die Datenbankabfrage. Erst das gesamt3-Attribut ermittelt durch den Type-Cast auf xs:decimal den korrekten Wert.
<?xml version="1.0" encoding="UTF-8"?>
<ergebnis>
<artikel name="Hemd">
<einkauf anzahl="11"
einzelpreis="12.99"
gesamt="142.89000000000002"
gesamt2="142.89000000000001"
gesamt3="142.89"/>
<einkauf anzahl="22"
einzelpreis="12.99"
gesamt="285.78000000000003"
gesamt2="285.78000000000003"
gesamt3="285.78"/>
<einkauf anzahl="44"
einzelpreis="12.99"
gesamt="571.56000000000006"
gesamt2="571.5600000000001"
gesamt3="571.56"/>
...
</artikel>
<artikel name="Hose">
<einkauf anzahl="8"
einzelpreis="25.99"
gesamt="207.92"
gesamt2="207.92"
gesamt3="207.92"/>
...
</artikel>
<artikel name="Schuhe">
<einkauf anzahl="6"
einzelpreis="151.23"
gesamt="907.37999999999988"
gesamt2="907.3799999999999"
gesamt3="907.38"/>
...
</artikel>
</ergebnis>
Statt group by können Sie auch mit for-each und distinct-values arbeiten. $k beinhaltet in diesem Fall die gruppierte Liste ("Hemd", "Hose", "Schuhe").
xquery version "3.0";
<ergebnis>
{
for $k in
for-each(
distinct-values(//Kauf/bez/text()),
function ($pk) {$pk}
)
return
<artikel
name="{$k}">
{
for $kk in //Kauf[bez/text() = $k]
return
<einkauf
anzahl="{$kk/anzahl}"
einzelpreis="{$kk/preis}"
gesamt3="{
xs:decimal($kk/anzahl)
* xs:decimal($kk/preis)
}"/>
}
</artikel>
}
</ergebnis>
Analog zu Gruppieren in XPath 1.0/XSL 1.0, ist eine vereinfachte Gruppierung auch in XQuery möglich.
<ergebnis>
{
for $a in //Kauf[not(bez = following::Kauf/bez)]
return
<artikel name="{$a/bez/text()}"/>
}
</ergebnis>
Der Vorteil dieses Ansatzes ist auch hier, dass der Zugriff auf den XML-Input nicht verloren geht: Nachdem Sie bez in der Variable vbez gesichert haben, können Sie sie auch in for $b in //Kauf[bez/text() = $vbez] verwenden.
xquery version "1.0";
<ergebnis>
{
for $a in //Kauf[not(bez/text() = following::Kauf/bez/text())]
let $vbez := $a/bez/text()
return
<artikel name="{$vbez}">
{
for $b in //Kauf[bez/text() = $vbez]
return
<einzelkauf>
{
for $c in $b/child::*
return
attribute {$c/name()} {$c/text()}
}
</einzelkauf>
}
</artikel>
}
</ergebnis>
wg / 26. Oktober 2020
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de