2010-11-24 47 views
1

J'ai une question sur le groupement xquery. D'après ce que je comprends, le cas habituel serait d'utiliser distinct-values sur les docs dans la boucle for, mais j'ai une condition à remplir avant de grouper les valeurs, et je ne suis pas sûr de savoir comment cela peut être fait.Résultats de regroupement Xquery basé sur la condition

Cela fait partie de mon xmldb:

<element tag="0001,0000" name="Pos1>198</element> 
<element tag="0001,0001" name="Pos2">123</name> 
<element tag="0002,0001" name="Pos3">433</element> 
<element tag="000b,0000" name="Pos3">16</element> 
<element tag="0005,0000" name="Pos4>532</element> 
<element tag="0005,0001" name="Pos5">342</name> 
<element tag="0008,0001" name="Pos6">17</element> 

La condition à remplir est que les coordonnées x (nombre ou hexdec) doivent être des valeurs impaires (par exemple à partir du XML ci-dessus, je seulement besoin résultats de tag = "0001, ...", tag = "000b, ...", tag = "0005, ..."), puis comptez combien d'éléments dans chaque groupe.

Voici comment les résultats devraient ressembler à:

<group> 
<element xcoord="0001">2</element> 
<element xcoord="000b">1</element> 
<element xcoord="0005">2</element> 
</group> 

Mon code XQuery donc ressemble beaucoup comme ça, où je pourrais générer des résultats qui ont les coordonnées x impaires, mais je ne sais pas comment procéder sur d'ici pour le groupement.

import module namespace functx="http://www.functx.com" at "http://www.xqueryfunctions.com/xq/functx-1.0-nodoc-2007-01.xq"; 

for $x in collection('/db/mapdb/')//element 
let $coord := number(functx:substring-before-last($x/@tag, ",")) 
where $coord mod 2 != 0 
return $x 

Veuillez me conseiller. Merci beaucoup.

+0

Bonne question, +1. Voir ma réponse pour une solution complète et correcte. –

Répondre

4

Ce XQuery:

for $x in 
    distinct-values(/*/*/substring-before(@tag,',') 
        [contains('13579bdf', 
           substring(., string-length(.)) 
          ) 
        ] 
       ) 
return 
    <element xcoord="{$x}"> 
    {count(/*/*[$x eq substring-before(@tag,',')])} 
    </element> 

produit le résultat souhaité, correct:

<element xcoord="0001">2</element> 
<element xcoord="000b">1</element> 
<element xcoord="0005">2</element> 
+0

+1 Merci, cela fonctionne, et assez cohérent. De même pour la solution d'Alejandro, j'ai besoin d'ajouter une boucle pour pouvoir faire une boucle pour faire la vérification par rapport à l'étiquette. Je me demande est-ce dû à l'eXist Xquery? –

+0

@jl: Je vérifie toujours mes solutions avant de les publier. Ces résultats ont été réellement produits - lorsqu'ils sont exécutés avec oXygen + Saxon 9.2.0.3. –

0

Vous n'avez pas indiqué le processeur que vous utilisez. Je l'ai testé le code suivant sur try.zorba-xquery.com (il devrait également fonctionner pour les processeurs saxons ou d'autres supportant XQuery 1.1 et XPath 2.0):

let $xmldb := 
    (
    <element tag="0001,0000" name="Pos1">198</element>, 
    <element tag="0001,0001" name="Pos2">123</element>, 
    <element tag="0002,0001" name="Pos3">433</element>, 
    <element tag="000b,0000" name="Pos3">16</element>, 
    <element tag="0005,0000" name="Pos4">532</element>, 
    <element tag="0005,0001" name="Pos5">342</element>, 
    <element tag="0008,0001" name="Pos6">17</element> 
) 
for $x in $xmldb 
let $coord := tokenize($x/@tag, ",")[1] 
group by $coord 
where contains('13579bdf',substring($coord,string-length($coord))) 
return <element xcoord="{$coord}">{count($x)}</element> 

Espoir qui aide?

+0

'où number ($ coord) mod 2! = 0' - cela ne fonctionne évidemment pas avec un nombre représenté dans un format hexadécimal. –

+0

L'OP n'a probablement pas de processeur XQuery 3.0, ce qui exclut la possibilité d'utiliser 'group by'. –

+0

J'utilise eXist DB pour le sandbox Xquery, mais je ne pense pas qu'il existe un groupe par fonction pour ce processeur, ce qui est très pratique avec SQL. –

0

Si vous utilisez un processeur XQuery 1.0 puis le schéma habituel pour un groupe en est:

let $values := ... 
for $key in distinct-values(for $value in $values return my:key($value)) 
let $group := $values[my:key(.)=$key] 
return ... 

my:key est une fonction/expression qui obtient la clé pour chaque valeur d'entrée.

Dans votre cas, vous pouvez remplir le motif comme si:

for $coord in distinct-values($xmldb/(tokenize(.,",")[1])) 
let $x := $xmldb[tokenize(.,",")[1] = $coord] 
return <element xcoord="{$coord}">{count($x)}</element> 

Que ce soit ou non effectuer dépend efficacement de votre mise en œuvre. J'ai écrit un blog post sur la façon d'écrire un groupe par ce que XQSharp reconnaîtra.

Édition: J'ai manqué l'exigence que toutes les coordonnées x doivent être impaires. Cela peut être corrigé par l'ajout d'une clause where pour contraindre le dernier caractère de coordonnées:

for $coord in distinct-values($xmldb/(tokenize(.,",")[1])) 
where contains('13579bdf',substring($coord,string-length($coord))) 
let $x := $xmldb[tokenize(.,",")[1] = $coord] 
return <element xcoord="{$coord}">{count($x)}</element> 
+0

Est-ce syntaxiquement correct: 'let $ x: = $ xmldb [tokenize (.,!,!) [1] = $ coord]'. En particulier, quel est le sens des points d'exclamation? –

+0

Cette réponse ne répond pas à l'exigence selon laquelle seules les valeurs impaires de la coordonnée x doivent être prises en compte dans le regroupement. –

+0

@Dimitre: Les points d'exclamation auraient dû être des guillemets - J'ai corrigé cette faute de frappe! –

3

Ce XQuery:

<group>{ 
    for $key in distinct-values(/root/element/tokenize(@tag,',')[1]) 
        [contains('13579bdf',substring(.,string-length(.)))] 
    return <element xcoord="{$key}">{ 
       count(/root/element[tokenize(@tag,',')[1] eq $key]) 
      }</element> 
}</group> 

Avec cette entrée:

<root> 
    <element tag="0001,0000" name="Pos1">198</element> 
    <element tag="0001,0001" name="Pos2">123</element> 
    <element tag="0002,0001" name="Pos3">433</element> 
    <element tag="000b,0000" name="Pos3">16</element> 
    <element tag="0005,0000" name="Pos4">532</element> 
    <element tag="0005,0001" name="Pos5">342</element> 
    <element tag="0008,0001" name="Pos6">17</element> 
</root> 

sortie:

<group> 
    <element xcoord="0001">2</element> 
    <element xcoord="000b">1</element> 
    <element xcoord="0005">2</element> 
</group> 

Remarque: Il n'y a pas d'opérateur intégré pour le type de données xs: hexBinary, sauf pour eq et ne.

+0

+1 pour la seule réponse correcte. Une remarque mineure est de ne pas utiliser l'opérateur général d'égalité - l'opérateur d'égalité des valeurs est le plus approprié ici et aussi plus efficace. Une autre remarque mineure concerne l'utilisation inutile de la plus longue surcharge de 'substring()' et le manque de cohérence - pourquoi n'utilisez-vous pas seulement 'substring()' ou seulement 'tokenize()'? –

+0

@Dimitre: Merci. Je prends vos remarques. A propos de la cohérence: mon objectif était de résoudre clairement le problème hexadécimal. –

+0

@Alejandro: Merci. Je suis en train de tester avec eXist Xquery sandbox, a eu un petit problème avec le nombre. Je lui ai ajouté une boucle comme: count (pour $ x dans/root/element/@ tag où (tokenize ($ x, ',') [1] eq $ key) renvoie $ x) et ça marche parfaitement merci. + 1 –