Alex Russell
Alex Russell

Reputation: 13

XQuery comparing variables as numbers

I want to produce an XQuery that outputs the price of three CDs, less the price of the cheapest CD. I have produced a user defined function sumtwomax() which takes three integer parameters and produces the sum of the largest two numbers. This works when I supply it with numbers. But I have a problem when supplying it with variables from a FLWOR expression. Could someone please help me with this?

Here is my XML code:

<items>
  <item>
    <code>c002</code>
    <price>10</price>
    <rating>5</rating>
  </item>
  <item>
    <code>c006</code>
    <price>15</price>
    <rating>3</rating>
  </item>
  <item>
    <code>c004</code>
    <price>12</price>
    <rating>3</rating>
  </item>
  <item>
    <code>c001</code>
    <price>7</price>
    <rating>5</rating>
  </item>
  <item>
    <code>c003</code>
    <price>10</price>
    <rating>4</rating>
  </item>
  <item>
    <code>c005</code>
    <price>8</price>
    <rating>4</rating>
  </item>
</items>

And here is my XQuery:

declare namespace myfn = "http://www.brookes.ac.uk/P00601/xquery/functions";
declare function myfn:sumtwomax( $first,  $sec, $third)  { sum(($first, $sec, $third)) - min (($first, $sec, $third))};

for $d in doc("shop.xml") //item
let $price1 := xs:integer($d/price/data()[$d/code="c002"]) 
let $price2 := xs:integer($d/price/data()[$d/code="c004"]) 
let $price3 := xs:integer($d/price/data()[$d/code="c006"])
return
myfn:sumtwomax($price1, $price2, $price3)

This produces '0 0 0' as a result, instead of the desired value '15'. Could someone please help with this?

Upvotes: 1

Views: 714

Answers (1)

Charles Duffy
Charles Duffy

Reputation: 295619

A FLWOR expression doesn't make any sense in this context: It's only evaluating a single item at a time (and returning one result per item), but the expressions you run inside that loop only return useful results if they can search through all the items, to be able to fill out all three variables (as opposed to only the variable associated with the single item being iterated over by the loop at that time).

Consider instead iterating over items elements (of which there's only one), if you really want to make this a FLWOR:

declare function myfn:sumtwomax( $first,  $sec, $third)  { sum(($first, $sec, $third)) - min (($first, $sec, $third))};

for $d in //items
let $price1 := xs:integer($d/item[code="c002"]/price)
let $price2 := xs:integer($d/item[code="c004"]/price) 
let $price3 := xs:integer($d/item[code="c006"]/price)
return myfn:sumtwomax($price1, $price2, $price3)

...or, removing the needless for entirely:

declare function myfn:sumtwomax( $first,  $sec, $third)  { sum(($first, $sec, $third)) - min (($first, $sec, $third))};

let $price1 := xs:integer(//item[code="c002"]/price)
let $price2 := xs:integer(//item[code="c004"]/price) 
let $price3 := xs:integer(//item[code="c006"]/price)
return myfn:sumtwomax($price1, $price2, $price3)

Upvotes: 2

Related Questions