0
votes

I'm trying to write an Xquery which has 2 parts:

  1. The first part is a function in Xquery to compute the number of students for each class element. the input of the function should be a class element and the output is the number of students who took that class.
  2. At the second part I want the list of courses which had at least one student (for finding the number of students for each course I have to use the function from the first part).

This is how the input xml file looks like: in the input file we have 3 different elements: Student, Class, and Course, which are related together.

<?xml version="1.0" ?>
<Report Date="1999-12-02">
 <Students>
 <Student StudId="s11">
 <Name><First>John</First><Last>Doe</Last></Name>
 <Status>U2</Status>
 <CrsTaken CrsCode="CS308" Semester="F1997"/> 
 <CrsTaken CrsCode="MAT123" Semester="F1997"/> 
 </Student>
 <Student StudId="s66">
 <Name><First>Joe</First><Last>Public</Last></Name>
 <Status>U2</Status> 
 <CrsTaken CrsCode="MAT123" Semester="F1997"/> 
 </Student>
 </Students>
 <Classes>
 <Class>
 <CrsCode>CS308</CrsCode><Semester>F1997</Semester>
 <ClassRoster Members="s11"/>
 </Class>
 <Class>
 <CrsCode>MAT123</CrsCode><Semester>F1997</Semester>
 <ClassRoster Members="s11 s66"/>
 </Class>
</Classes>
 <Courses>
 <Course CrsCode="CS308">
 <CrsName>Software Engineering</CrsName>
 </Course> 
 <Course CrsCode="MAT123">
 <CrsName>ALgebra</CrsName>
 </Course>
 </Courses>
</Report>

And this is the code I've done. I wrote a function which has an element as input and will return an integer as number of students. I also tried to call the function for the second part. But my query has a problem.

declare function local:numstudents($e as element())as xs:integer
{for $s in doc('test')//Student
 let $T:=$e[CrsCode/text()=$s/CrsTaken/@CrsCode and Semester/text()=$s/CrsTaken/@Semester]
 where not(fn:empty($T))
 return count($s) };

<Courses>
 {for $co in doc('test')//Course,
    $c in doc('test')//Class
   where $co/@CrsCode=$c/CrsCode/text() and local:numstudents($c)>1
 return <course CrsCode="{$co/@CrsCode}" num="{$co/CrsName/text()}" />}
</Courses> 

The Problem is for function part which return a sequence instead of an integer. This is the error: (Single item expected, (1, 1) found). I'm using http://basex.org/products/live-demo/ for testing my xquery. Is there any help? Please if it is possible correct my code instead of writing a new code :). It should be a simple query but I don't know how I can fix it :)

1

1 Answers

0
votes

The problem is in the function, where you're calling count for each student instead counting them all.

declare function local:numstudents($e as element())as xs:integer
{
  count(
    for $s in doc('test')//Student
    let $T:=$e[CrsCode/text()=$s/CrsTaken/@CrsCode and Semester/text()=$s/CrsTaken/@Semester]
    where not(fn:empty($T))
    return $s
  )
};

Some more hints:

  • You should be able to remove most of the text(...) calls. Most of the time, they're unnecessary or even wrong. For comparisons and other calls, XQuery will automatically fetch the data value of an element, otherwise better use data(...) instead.
  • not(fn:empty($T)) is the same as $T if it gets evaluated to a boolean value anyway.
  • Better stay consistent with functional calls and using the fn: prefix.
  • It's rather hard to understand what's going on with those very short variable names. Why not use $course, $class and $student?