Combining and Restructuring Nodes

Queries in XQuery often combine information from one or more sources and restructure it to create a new result. This section focuses on the expressions and functions most commonly used for combining and restructuring XML data.

FLWOR Expressions

FLWOR expressions, pronounced "flower expressions," are one of the most powerful and common expressions in XQuery. They are similar to the SELECT-FROM-WHERE statements in SQL . However, a FLWOR expression is not defined in terms of tables, rows, and columns ; instead, a FLWOR expression binds variables to values in for and let clauses, and uses these variable bindings to create new results. A combination of variable bindings created by the for and let clauses of a FLWOR expression is called a tuple.

For instance, here is a simple FLWOR expression that returns the title and price of each book that was published in the year 2000:

 for $b in doc("books.xml")//book where $b/@year = "2000" return $b/title 

This query binds the variable $b to each book, one at a time, to create a series of tuples. Each tuple contains one variable binding in which $b is bound to a single book. The where clause tests each tuple to see if $b/@year is equal to "2000," and the return clause is evaluated for each tuple that satisfies the conditions expressed in the where clause. In our sample data, only Data on the Web was written in 2000, so the result of this query is

 <title>Data on the Web</title> 

The name FLWOR is an acronym, standing for the first letter of the clauses that may occur in a FLWOR expression:

  • for clauses : associate one or more variables to expressions, creating a tuple stream in which each tuple binds a given variable to one of the items to which its associated expression evaluates

  • let clauses : bind variables to the entire result of an expression, adding these bindings to the tuples generated by a for clause, or creating a single tuple to contain these bindings if there is no for clause

  • where clauses : filter tuples, retaining only those tuples that satisfy a condition

  • order by clauses : sort the tuples in a tuple stream

  • return clauses : build the result of the FLWOR expression for a given tuple

The acronym FLWOR roughly follows the order in which the clauses occur. A FLWOR expression starts with one or more for or let clauses in any order, followed by an optional where clause, an optional order by clause, and a required return clause.

The for and let Clauses

Every clause in a FLWOR expression is defined in terms of tuples, and the for and let clauses create the tuples. Therefore, every FLWOR expression must have at least one for or let clause. It is extremely important to understand how tuples are generated in FLWOR expressions, so we will start with a series of artificial queries that show this in detail for various combinations of for clauses and let clauses.

We have already shown an example that binds one variable in a for clause. The following query creates an element named tuple in its return clause to show the tuples generated by such a query:

 for $i in (1, 2, 3) return    <tuple><i>{ $i }</i></tuple> 

In this example, we bind $i to the expression (1, 2, 3), which constructs a sequence of integers. XQuery has a very general syntax, and for clauses or let clauses can be bound to any XQuery expression. Here is the result of the above query, showing how the variable $i is bound in each tuple:

 <tuple><i>1</i></tuple> <tuple><i>2</i></tuple> <tuple><i>3</i></tuple> 

Note that the order of the items bound in the tuple is the same as the order of the items in the original expression (1, 2, 3) . A for clause preserves order when it creates tuples.

A let clause binds a variable to the entire result of an expression. If there are no for clauses in the FLWOR expression, then a single tuple is created, containing the variable bindings from the let clauses. The following query is like the previous query, but it uses a let clause rather than a for :

 let $i := (1, 2, 3) return    <tuple><i>{ $i }</i></tuple> 

The result of this query contains only one tuple, in which the variable $i is bound to the entire sequence of integers:

 <tuple><i>1 2 3</i></tuple> 

If a let clause is used in a FLWOR expression that has one or more for clauses, the variable bindings of let clauses are added to the tuples generated by the for clauses. This is demonstrated by the following query:

 for $i in (1, 2, 3) let $j := (1, 2, 3) return    <tuple><i>{ $i }</i><j>{ $j }</j></tuple> 

If a let clause is used in a FLWOR expression that has one or more for clauses, the variable bindings from let clauses are added to the tuples generated by the for clauses:

 <tuple><i>1</i><j>1 2 3</j></tuple> <tuple><i>2</i><j>1 2 3</j></tuple> <tuple><i>3</i><j>1 2 3</j></tuple> 

Here is a query that combines for and let clauses in the same way as the previous query:

 for $b in doc("books.xml")//book let $c := $b/author return <book>{ $b/title, <count>{ count($c) }</count>}</book> 

This query lists the title of each book together with the number of authors. Listing 1.3 shows the result when we apply it to our bibliography data.

Listing 1.3 Query Results
 <book>   <title>TCP/IP Illustrated</title>   <count>1</count> </book> <book>   <title>Advanced Programming in the UNIX Environment</title>   <count>1</count> </book> <book>   <title>Data on the Web</title>   <count>3</count> </book> <book>   <title>The Economics of Technology and Content for   Digital TV</title>   <count>0</count> </book> 

If more than one variable is bound in the for clauses of a FLWOR expression, then the tuples contain all possible combinations of the items to which these variables are bound. For instance, the following query shows all combinations that include 1, 2, or 3 combined with 4, 5, or 6:

 for $i in (1, 2, 3),     $j in (4, 5, 6) return    <tuple><i>{ $i }</i><j>{ $j }</j></tuple> 

Here is the result of the above query:

 <tuple><i>1</i><j>4</j></tuple> <tuple><i>1</i><j>5</j></tuple> <tuple><i>1</i><j>6</j></tuple> <tuple><i>2</i><j>4</j></tuple> <tuple><i>2</i><j>5</j></tuple> <tuple><i>2</i><j>6</j></tuple> <tuple><i>3</i><j>4</j></tuple> <tuple><i>3</i><j>5</j></tuple> <tuple><i>3</i><j>6</j></tuple> 

A combination of all possible combinations of sets of values is called a Cartesian cross-product. The tuples preserve the order of the original sequences, in the order in which they are bound. In the previous example, note that the tuples reflect the values of each $i in the original order; for a given value of $i , the values of $j occur in the original order. In mathematical terms, the tuples generated in a FLWOR expression are drawn from the ordered Cartesian cross-product of the items to which the for variables are bound.

The ability to create tuples that reflect combinations becomes particularly interesting when combined with where clauses to perform joins . The following sections illustrate this in depth. But first we must introduce the where and return clauses.

The where Clause

A where clause eliminates tuples that do not satisfy a particular condition. A return clause is only evaluated for tuples that survive the where clause. The following query returns only books whose prices are less than $50.00:

 for $b in doc("books.xml")//book where $b/price < 50.00 return $b/title 

Here is the result of this query:

 <title>Data on the Web</title> 

A where clause can contain any expression that evaluates to a Boolean value. In SQL, a WHERE clause can only test single values, but there is no such restriction on where clauses in XQuery. The following query returns the title of books that have more than two authors:

 for $b in doc("books.xml")//book let $c := $b//author where count($c) > 2 return $b/title 

Here is the result of the above query:

 <title>Data on the Web</title> 
The order by Clause

The order by clause sorts the tuples before the return clause is evaluated in order to change the order of results. For instance, the following query lists the titles of books in alphabetical order:

 for $t in doc("books.xml")//title order by $t return $t 

The for clause generates a sequence of tuples, with one title node in each tuple. The order by clause sorts these tuples according to the value of the title elements in the tuples, and the return clause returns the title elements in the same order as the sorted tuples. The result of this query is

 <title>Advanced Programming in the Unix Environment</title> <title>Data on the Web</title> <title>TCP/IP Illustrated</title> <title>The Economics of Technology and Content for Digital TV</title> 

The order by clause allows one or more orderspecs, each of which specifies one expression used to sort the tuples. An orderspec may also specify whether to sort in ascending or descending order, how expressions that evaluate to empty sequences should be sorted, a specific collation to be used, and whether stable sorting should be used (stable sorting preserves the relative order of two items if their values are equal). Here is a query that returns authors, sorting in reverse order by the last name, then the first name:

 for $a in doc("books.xml")//author order by $a/last descending, $a/first descending return $a 

The result of this query is shown in Listing 1.4.

Listing 1.4 Results of Query for Authors Sorted by Last Name
 <author>     <last>Suciu</last>     <first>Dan</first> </author> <author>     <last>Stevens</last>     <first>W.</first> </author> <author>     <last>Stevens</last>     <first>W.</first> </author> <author>     <last>Buneman</last>     <first>Peter</first> </author> <author>     <last>Abiteboul</last>     <first>Serge</first> </author> 

The order by clause may specify conditions based on data that is not used in the return clause, so there is no need for an expression to return data in order to use it to sort. Here is an example that returns the titles of books, sorted by the name of the first author:

 let $b := doc("books.xml")//book for $t in distinct-values($b/title) let $a1 := $b[title=$t]/author[1] order by $a1/last, $a1/first return $b/title 

The result of this query is

 <title>The Economics of Technology and Content for Digital TV</title> <title>Data on the Web</title> <title>Advanced Programming in the UNIX Environment</title> <title>TCP/IP Illustrated</title> 

The first book in this list has editors, but no authors. For this book, $a1/last and $a1/first will both return empty sequences. Some XQuery implementations always sort empty sequences as the greatest possible value; others always sort empty sequences as the least possible value. The XML Query Working Group decided to allow vendors to choose which of these orders to implement because many XQuery implementations present views of relational data, and relational databases differ in their sorting of nulls . To guarantee that an XQuery uses the same sort order across implementations, specify "empty greatest" or "empty least" in an orderspec if its expression can evaluate to an empty sequence.

Two books in our data are written by the same author, and we may want to ensure that the original order of these two books is maintained . We can do this by specifying a stable sort, which maintains the relative order of two items if the comparison expressions consider them equal. The following query specifies a stable sort, and requires empty sequences to be sorted as least:

 let $b := doc("books.xml")//book for $t in distinct-values($b/title) let $a1 := $b[title=$t]/author[1] stable order by $a1/last empty least, $a1/first empty least return $b/title 

This query returns the same result as the previous one, but is guaranteed to do so across all implementations.

Collations may also be specified in an order by clause. The following query sorts titles using a U.S. English collation:

 for $t in doc("books.xml")//title order by $t collation "http://www.example.com/collations/eng-us" return $t 

Most queries use the same collation for all comparisons, and it is generally too tedious to specify a collation for every orderspec. XQuery allows a default collation to be specified in the prolog. The default collation is used when the orderspec does not specify a collation. Here is a query that sets http://www.example.com/collations/eng-us as the default collation; it returns the same results as the previous query:

 default collation = "http://www.example.com/collations/eng-us" for $t in doc("books.xml")//title order by $t return $t 

When sorting expressions in queries, it is important to remember that the / and // operators sort in document order. That means that an order established with an order by clause can be changed by expressions that use these operators. For instance, consider the following query:

 let $authors := for $a in doc("books.xml")//author                 order by $a/last, $a/first                 return $a return $authors/last 

This query does not return the author's last names in alphabetical order, because the / in $authors/last sorts the last elements in document order. This kind of error generally occurs with let bindings, not with for bindings, because a for clause binds each variable to a single value in a given tuple, and returning children or descendents of a single node does not lead to surprises . The following query returns author's last names in alphabetical order:

 for $a in doc("books.xml")//author order by $a/last, $a/first return $a/last 
The return Clause

We have already seen that a for clause or a let clause may be bound to any expression, and a where clause may contain any Boolean expression. Similary, any XQuery expression may occur in a return clause. Element constructors are an extremely common expression in return clauses; for instance, the following query uses an element constructor to create price quotes:

 for $b in doc("books.xml")//book return   <quote>{ $b/title, $b/price }</quote> 

Listing 1.5 shows the result of the above query.

Listing 1.5 Results of Query for Price Quotes
 <quote>    <title>TCP/IP Illustrated</title>    <price>65.95</price> </quote> <quote>    <title>Advanced Programming in the UNIX Environment</title>    <price>65.95</price> </quote> <quote>    <title>Data on the Web</title>    <price>39.95</price> </quote> <quote>    <title>The Economics of Technology and Content for Digital TV</title>    <price>129.95</price> </quote> 

Element constructors can be used in a return clause to change the hierarchy of data. For instance, we might want to represent an author's name as a string in a single element, which we can do with the following query:

 for $a in doc("books.xml")//author return   <author>{ string($a/first), " ", string($a/last) }</author> 

Here is the result of the above query:

 <author>W. Stevens</author> <author>W. Stevens</author> <author>Serge Abiteboul</author> <author>Peter Buneman</author> <author>Dan Suciu</author> 

Another application might want to insert a name element to hold the first and last name of the author ”after all, an author does not consist of a first and a last! Here is a query that adds a level to the hierarchy for names:

 for $a in doc("books.xml")//author return <author>    <name>{ $a/first, $a/last }</name>   </author> 

Here is one author's name taken from the output of the above query:

 <author>    <name>    <first>Serge</first>    <last>Abiteboul</last>    </name> </author> 

This section has discussed the most straightforward use of for and return clauses, and it has shown how to combine FLWOR expressions with other expressions to perform common tasks . More complex uses of for clauses are explored later in separate sections on joins and positional variables.

The Positional Variable at

The for clause supports positional variables, which identify the position of a given item in the expression that generated it. For instance, the following query returns the titles of books, with an attribute that numbers the books:

 for $t at $i in doc("books.xml")//title return <title pos="{$i}">{string($t)}</title> 

Here is the result of this query:

 <title pos="1">TCP/IP Illustrated</title> <title pos="2">Advanced Programming in the Unix Environment</title> <title pos="3">Data on the Web</title> <title pos="4">The Economics of Technology and Content for Digital TV</title> 

In some data, position conveys meaning. In tables, for instance, the row and column in which an item is found often determine its meaning. For instance, suppose we wanted to create data from an XHTML web page that contains the table shown in Table 1.2.

Table 1.2. Table from an XHTML Web Page

Title

Publisher

Price

Year

TCP/IP Illustrated

Addison-Wesley

65.95

1994

Advanced Programming in the UNIX Environment

Addison-Wesley

65.95

1992

Data on the Web

Morgan Kaufmann Publishers

39.95

2000

The Economics of Technology and Content for Digital TV

Kluwer Academic Publishers

129.95

1999

The XHTML source for this table is shown in Listing 1.2.

Listing 1.6 XHTML Source for Table 1.2
 <table border="1">     <thead>        <tr>               <td>Title</td>               <td>Publisher</td>               <td>Price</td>               <td>Year</td>        </tr>     </thead>     <tbody>        <tr>               <td>TCP/IP Illustrated</td>               <td>Addison-Wesley</td>               <td>65.95</td>               <td>1994</td>        </tr>        <tr>               <td>Advanced Programming in the UNIX               Environment</td>               <td>Addison-Wesley</td>               <td>65.95</td>               <td>1992</td>        </tr>    <! Additional rows omitted to save space >     </tbody> </table> 

In this table, every entry in the same column as the Title header is a title, every entry in the same column as the Publisher header is a publisher, and so forth. In other words, we can determine the purpose of an entry if we can determine its position as a column of the table, and relate it to the position of a column header. Positional variables make this possible. Since XHTML is XML, it can be queried using XQuery. Listing 1.7 shows a query that produces meaningful XML from the above data, generating the names of elements from the column headers.

Listing 1.7 Query to Generate Names of Elements from Column Headers
 let $t := doc("bib.xhtml")//table[1] for $r in $t/tbody/tr return   <book>    {      for $c at $i in $r/td      return element{ lower-case(data($t/thead/tr/td[$i])) }                    { string($c) }    }   </book> 

Note the use of a computed element constructor that uses the column header to determine the name of the element. Listing 1.8 shows the portion of the output this query generates for the partial data shown in Table 1.2.

Listing 1.8 Output Generated by the Query of Listing 1.7
 <book>    <title>TCP/IP Illustrated</title>    <publisher>Addison-Wesley</publisher>    <price>65.95</price>    <year>1994</year> </book> <book>    <title>Advanced Programming in the Unix Environment</title>    <publisher>Addison-Wesley</publisher>    <price>65.95</price>    <year>1992</year> </book> 
Eliminating Duplicate Subtrees with distinct-values() and FLWOR Expressions

Data often contains duplicate values, and FLWOR expressions are often combined with the distinct-values() function to remove duplicates from subtrees. Let's start with the following query, which returns the last name of each author:

 doc("books.xml")//author/last 

Since one of our authors wrote two of the books in the bibliography, the result of this query contains a duplicate:

 <last>Stevens</last> <last>Stevens</last> <last>Abiteboul</last> <last>Buneman</last> <last>Suciu</last> 

The distinct-values() function extracts the values of a sequence of nodes and creates a sequence of unique values, eliminating duplicates. Here is a query that uses distinct-values() to eliminate duplicate last names:

 distinct-values(doc("books.xml")//author/last) 

Here is the output of the above query:

 Stevens Abiteboul Buneman Suciu 

The distinct-values() function eliminates duplicates, but in order to do so, it extracts values from nodes. FLWOR expressions are often used together with distinct-values() to create subtrees that correspond to sets of one or more unique values. For the preceding query, we can use an element constructor to create a last element containing each value:

 for $l in distinct-values(doc("books.xml")//author/last) return <last>{ $l }</last> 

Here is the output of the above query:

 <last>Stevens</last> <last>Abiteboul</last> <last>Buneman</last> <last>Suciu</last> 

The same problem arises for complex subtrees. For instance, the following query returns authors, and one of the authors is a duplicate by both first and last name:

 doc("books.xml")//author 

The output of the above query appears in Listing 1.9.

Listing 1.9 Output of the Query for Authors
 <authors>     <author>       <last>Stevens</last>       <first>W.</first>     </author>     <author>       <last>Stevens</last>       <first>W.</first>     </author>     <author>       <last>Abiteboul</last>       <first>Serge</first>     </author>     <author>       <last>Buneman</last>       <first>Peter</first>     </author>     <author>       <last>Suciu</last>       <first>Dan</first>     </author>   </authors> 

To eliminate duplicates from complex subtrees, we have to decide what criterion to use for detecting a duplicate. In this case, let's say that an author is a duplicate if there is another author who has the same first and last names. Now let's write a query that returns one author for each first and last name that occur together within an author element in our dataset:

 let $a := doc("books.xml")//author for $l in distinct-values($a/last),     $f in distinct-values($a[last=$l]/first) return     <author>       <last>{ $l }</last>       <first>{ $f }</first>     </author> 

In the output of the above query (Listing 1.10), each author's name appears only once.

Listing 1.10 Output of Query to Avoid Duplicate Author Names
 <authors>     <author>       <last>Stevens</last>       <first>W.</first>     </author>     <author>       <last>Abiteboul</last>       <first>Serge</first>     </author>     <author>       <last>Buneman</last>       <first>Peter</first>     </author>     <author>       <last>Suciu</last>       <first>Dan</first>     </author>   </authors> 
Joins: Combining Data Sources with for and where Clauses

A query may bind multiple variables in a for clause in order to combine information from different expressions. This is often done to bring together information from different data sources. For instance, suppose we have a file named reviews.xml that contains book reviews:

 <reviews>   <entry>    <title>TCP/IP Illustrated</title>    <rating>5</rating>    <remarks>Excellent technical content. Not much plot.</remarks>   </entry> </reviews> 

A FLWOR expression can bind one variable to our bibliography data and another to the reviews, making it possible to compare data from both files and to create results that combine their information. For instance, a query could return the title of a book and any remarks found in a review.

As we have discussed earlier, the Cartesian cross-product of two sequences contains all possible combinations of the items in those sequences. When a where clause is used to select interesting combinations from the Cartesian cross-product, this is known as a join. The following query performs a join to combine data from a bibliography with data from a set of reviews:

 for $t in doc("books.xml")//title,     $e in doc("reviews.xml")//entry where $t = $e/title return <review>{ $t, $e/remarks }</review> 

The result of this query is as follows:

 <review>     <title>TCP/IP Illustrated</title>     <remarks>Excellent technical content. Not much plot.</remarks> </review> 

In this query, the for clauses create tuples from the Cartesian cross-product of titles and entries, the where clause filters out tuples where the title of the review does not match the title of the book, and the return clause constructs the result from the remaining tuples. Note that only books with reviews are shown. SQL programmers will recognize the preceding query as an inner join, returning combinations of data that satisfy a condition.

The tuples generated for a FLWOR expression include all expressions bound in variable bindings in for clauses. A FLWOR expression with multiple for clauses has the same meaning as a FLWOR expression that binds multiple variables in a single for clause. The following query is precisely equivalent to the previous one:

 for $t in doc("books.xml")//title for $e in doc("reviews.xml")//entry where $t = $e/title return <review>{ $t, $e/remarks }</review> 

The query shown in Listing 1.11 returns the title of each book regardless of whether it has a review; if a book does have a review, the remarks found in the review are also included. SQL programmers will recognize this as a left outer join .

Listing 1.11 Query to Return Titles with or without Reviews
 for $t in doc("books.xml")//title return   <review>    { $t }    {      for $e in doc("reviews.xml")//entry      where $e/title = $t      return $e/remarks    }   </review> 
Inverting Hierarchies

XQuery can be used to do quite general transformations. One transformation that is used in many applications is colloquially referred to as "inverting a hierarchy" ”creating a new document in which the top nodes represent information which was found in the lower nodes of the original document. For instance, in our sample data, publishers are found at the bottom of the hierarchy, and books are found near the top. Listing 1.12 shows a query that creates a list of titles published by each publisher, placing the publisher at the top of the hierarchy and listing the titles of books at the bottom.

Listing 1.12 Query to List Titles by Publisher
 <listings>   {     for $p in distinct-values(doc("books.xml")//publisher)     order by $p     return         <result>             { $p }             {                 for $b in doc("books.xml")/bib/book                 where $b/publisher = $p                 order by $b/title                 return $b/title             }         </result>   } </listings> The results of this query are as follows: <listings>  <result>      <publisher>Addison-Wesley</publisher>    <title>Advanced Programming in the Unix Environment</title>    <title>TCP/IP Illustrated</title>     </result>     <result>    <publisher>Kluwer Academic Publishers</publisher>    <title>The Economics of Technology and Content for    Digital TV</title>     </result>     <result>    <publisher>Morgan Kaufmann Publishers</publisher>    <title>Data on the Web</title>     </result> </listings> 

A more complex example of inverting a hierarchy is discussed in the following section on quantifiers.

Quantifiers

Some queries need to determine whether at least one item in a sequence satisfies a condition, or whether every item in a sequence satisfies a condition. This is done using quantifiers. An existential quantifier tests whether at least one item satisfies a condition. The following query shows an existential quantifier in XQuery:

 for $b in doc("books.xml")//book where some $a in $b/author       satisfies ($a/last="Stevens" and $a/first="W.") return $b/title 

The some quantifier in the where clause tests to see if there is at least one author that satisfies the conditions given inside the parentheses. Here is the result of the above query:

 <title>TCP/IP Illustrated</title> <title>Advanced Programming in the Unix Environment</title> 

A universal quantifier tests whether every node in a sequence satisfies a condition. The following query tests to see if every author of a book is named W. Stevens:

 for $b in doc("books.xml")//book where every $a in $b/author       satisfies ($a/last="Stevens" and $a/first="W.") return $b/title 

Here is the result of the above query:

 <title>TCP/IP Illustrated</title> <title>Advanced Programming in the Unix Environment</title> <title>The Economics of Technology and Content for Digital TV</title> 

The last title returned, The Economics of Technology and Content for Digital TV , is the title of a book that has editors but no authors. For this book, the expression $b/author evaluates to an empty sequence. If a universal quantifier is applied to an empty sequence, it always returns true, because every item in that (empty) sequence satisfies the condition ”even though there are no items.

Quantifiers sometimes make complex queries much easier to write and understand. For instance, they are often useful in queries that invert hierarchies. Listing 1.13 shows a query that creates a list of books written by each author in our bibliography.

Listing 1.13 Query to List Books by Author
 <author-list>   {     let $a := doc("books.xml")//author     for $l in distinct-values($a/last),         $f in distinct-values($a[last=$l]/first)     order by $l, $f     return         <author>           <name>{ $l, ", ", $f }</name>           {              for $b in doc("books.xml")/bib/book              where some $ba in $b/author satisfies                    ($ba/last=$l and $ba/first=$f)              order by $b/title              return $b/title           }         </author>   } </author-list> 

The result of the above query is shown in Listing 1.14.

Listing 1.14 Results of Query to List Books by Author
 <author-list>     <author>        <name>Stevens, W.</name>        <title>Advanced Programming in the Unix Environment</title>        <title>TCP/IP Illustrated</title>     </author>     <author>        <name>Abiteboul, Serge</name>        <title>Data on the Web</title>     </author>     <author>        <name>Buneman, Peter</name>        <title>Data on the Web</title>     </author>     <author>        <name>Suciu, Dan</name>        <title>Data on the Web</title>     </author> </author-list> 

Conditional Expressions

XQuery's conditional expressions are used in the same way as conditional expressions in other languages. Listing 1.15 shows a query that uses a conditional expression to list the first two authors' names for each book and a dummy name containing "et al." to represent any remaining authors.

Listing 1.15 Query to List Author's Names with "et al."
 for $b in doc("books.xml")//book return   <book>     { $b/title }     {       for $a at $i in $b/author       where $i <= 2       return <author>{string($a/last), ", ",                      string($a/first)}</author>     }     {       if (count($b/author) > 2)       then <author>et al.</author>       else ()     }    </book> 

In XQuery, both the then clause and the if clause are required. Note that the empty sequence () can be used to specify that a clause should return nothing. The output of this query is shown in Listing 1.16.

Listing 1.16 Result of Query from Listing 1.15
 <book>     <title>TCP/IP Illustrated</title>     <author>Stevens, W.</author> </book> <book>     <title>Advanced Programming in the Unix Environment</title>     <author>Stevens, W.</author> </book> <book>     <title>Data on the Web</title>     <author>Abiteboul, Serge</author>     <author>Buneman, Peter</author>     <author>et al.</author> </book> <book>     <title>The Economics of Technology and Content for     Digital TV</title> </book> 


XQuery from the Experts(c) A Guide to the W3C XML Query Language
Beginning ASP.NET Databases Using VB.NET
ISBN: N/A
EAN: 2147483647
Year: 2005
Pages: 102

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net