Introduction to XQuery
by K. Yue
1. Introduction
Resources:
Basics:
Comparisons to SQL:
Relational DB: SQL | XML DB: XQuery | |
Basic Unit | relations | collections |
Records | tuples or rows of the schema | documents of the same schema |
Schema | Relational Schema | XML Schema |
Query Result | Relations: ordered set of rows | Ordered sequence of (mostly) nodes. |
XQuery Data Types
Query Expressions
Functions
Prolog
Example: Variable declaration
Format: declare variable $name = expression;
declare variable $a := doc("census.xml")//person ;
Path Expressions
Example: In Editix,
Use "View > Windows > XQuery Builder" For XQ files
Use "XSLT/XQuery > Transform using an XQuery Request…"
Specify source xq file, xml file and output file. Use .xml extension. If you use .txt extension, only text node contents are output.
declare base-uri "whatever-path";
doc("bib.xml")/*
Return basically bib.xml.
doc("bib.xml")//*
Element Constructors
Example: The XQuery
<authors>
<author>Bun Yue</author>
</authors>
returns
<authors>
<author>Bun Yue</author>
</authors>
Example:
Using film.xml:
<films>
<titles>
{ //film/title }
</titles>
</films>
returns:
<?xml version="1.0" encoding="UTF-8"?>
<films>
<titles>
<title>ACADEMY DINOSAUR</title>
<title>ACE GOLDFINGER</title>
<title>ADAPTATION HOLES</title>
...
<title>ZOOLANDER FICTION</title>
<title>ZORRO ARK</title>
</titles>
</films>
Example:
<films>
{ element titles
{ //film/title }
}
</films>
return the same result as the previous example.
element films {
attribute numFilms {
count(//film)
}
}
returns
<?xml version="1.0" encoding="UTF-8"?>
<films numFilms="1000"/>
<categories>
{ for $c in fn:distinct-values(//category/text())
return
<category>
<name>{ $c }</name>
</category>
}
</categories>
returns
<?xml version="1.0" encoding="UTF-8"?>
<categories>
<category>
<name>Documentary</name>
</category>
<category>
<name>Horror</name>
</category>
<category>
<name>Family</name>
</category>
<category>
<name>Foreign</name>
</category>
<category>
<name>Comedy</name>
</category>
<category>
<name>Sports</name>
</category>
<category>
<name>Music</name>
</category>
<category>
<name>Classics</name>
</category>
<category>
<name>Animation</name>
</category>
<category>
<name>Action</name>
</category>
<category>
<name>New</name>
</category>
<category>
<name>Sci-Fi</name>
</category>
<category>
<name>Drama</name>
</category>
<category>
<name>Travel</name>
</category>
<category>
<name>Games</name>
</category>
<category>
<name>Children</name>
</category>
</categories>
Example:
<categories>
{ for $c in fn:distinct-values(//category/text())
return
<category>
<name>{ $c }</name>
{ element numFiles
{ count(//film/category/text()=$c) }
}
</category>
}
</categories>
returns:
<?xml version="1.0" encoding="UTF-8"?>
<categories>
<category>
<name>Documentary</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Horror</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Family</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Foreign</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Comedy</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Sports</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Music</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Classics</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Animation</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Action</name>
<numFiles>1</numFiles>
</category>
<category>
<name>New</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Sci-Fi</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Drama</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Travel</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Games</name>
<numFiles>1</numFiles>
</category>
<category>
<name>Children</name>
<numFiles>1</numFiles>
</category>
</categories>
The XQuery:
<categories>
{ for $c in fn:distinct-values(//category/text())
return
element { $c }
{ attribute numFiles
{ count(//film[category/text()=$c]) }
}
}
</categories>
returns:
<?xml version="1.0" encoding="UTF-8"?>
<categories>
<Documentary numFiles="68"/>
<Horror numFiles="56"/>
<Family numFiles="69"/>
<Foreign numFiles="73"/>
<Comedy numFiles="58"/>
<Sports numFiles="74"/>
<Music numFiles="51"/>
<Classics numFiles="57"/>
<Animation numFiles="66"/>
<Action numFiles="64"/>
<New numFiles="63"/>
<Sci-Fi numFiles="61"/>
<Drama numFiles="62"/>
<Travel numFiles="57"/>
<Games numFiles="61"/>
<Children numFiles="60"/>
</categories>
FLWOR expressions
From W3C:
[42] FLWORExpr ::= (ForClause | LetClause)+ WhereClause? OrderByClause? "return" ExprSingle
[43] ForClause ::= "for" "$" VarName TypeDeclaration? PositionalVar? "in" ExprSingle ("," "$" VarName TypeDeclaration? PositionalVar? "in" ExprSingle)*
[45] LetClause ::= "let" "$" VarName TypeDeclaration? ":=" ExprSingle ("," "$" VarName TypeDeclaration? ":=" ExprSingle)*
[123] TypeDeclaration ::= "as" SequenceType
[44] PositionalVar ::= "at" "$" VarName
[46] WhereClause ::= "where" Expr
[47] OrderByClause ::= ("order" "by" | "stable" "order" "by") OrderSpecList
[48] OrderSpecList ::= OrderSpec ("," OrderSpec)*
[49] OrderSpec ::= ExprSingle OrderModifier
[50] OrderModifier ::= ("ascending" | "descending")? (("empty" "greatest") | ("empty" "least"))? ("collation" StringLiteral)?
Example:
List the film categories with 60 or more films in ascending orders of category names.
<categories>
{ for $cat in fn:distinct-values(//category/text())
let $count := count(//film[category/text()=$cat])
where $count >= 60
order by $cat
return
element { $cat }
{ attribute numFiles
{ $count }
}
}
</categories>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<categories>
<Action numFiles="64"/>
<Animation numFiles="66"/>
<Children numFiles="60"/>
<Documentary numFiles="68"/>
<Drama numFiles="62"/>
<Family numFiles="69"/>
<Foreign numFiles="73"/>
<Games numFiles="61"/>
<New numFiles="63"/>
<Sci-Fi numFiles="61"/>
<Sports numFiles="74"/>
</categories>
List ids of actors 'KILMER'.
XQuery:
element actors
{ for $aid in fn:distinct-values
(//actor[text()[contains(.,'KILMER')]]/@id)
return
element actor
{ attribute id { $aid }
}
}
Output:
<?xml version="1.0" encoding="UTF-8"?>
<actors>
<actor id="162"/>
<actor id="23"/>
<actor id="55"/>
<actor id="45"/>
<actor id="153"/>
</actors>
List names and ids of actors 'KILMER'.
XQuery:
element actors
{ for $aid in fn:distinct-values
(//actor[text()[contains(.,'KILMER')]]/@id)
let $aname := (//actor[@id=$aid][1]/text())[1]
return
element actor
{ attribute id { $aid },
element name { $aname }
}
}
Output:
<?xml version="1.0" encoding="UTF-8"?>
<actors>
<actor id="162">
<name>OPRAH KILMER</name>
</actor>
<actor id="23">
<name>SANDRA KILMER</name>
</actor>
<actor id="55">
<name>FAY KILMER</name>
</actor>
<actor id="45">
<name>REESE KILMER</name>
</actor>
<actor id="153">
<name>MINNIE KILMER</name>
</actor>
</actors>
List names, ids and number of films of actors 'KILMER'.
XQuery:
element actors
{ for $aid in fn:distinct-values
(//actor[text()[contains(.,'KILMER')]]/@id)
let $name := (//actor[@id=$aid]/text())[1],
$n_films := count(//film[actor/@id = $aid])
order by number($n_films) descending
return
element actor
{ attribute id { $aid },
element name { $name },
element nFilms { $n_films }
}
}
Output:
<?xml version="1.0" encoding="UTF-8"?>
<actors>
<actor id="23">
<name>SANDRA KILMER</name>
<nFilms>37</nFilms>
</actor>
<actor id="45">
<name>REESE KILMER</name>
<nFilms>32</nFilms>
</actor>
<actor id="162">
<name>OPRAH KILMER</name>
<nFilms>25</nFilms>
</actor>
<actor id="55">
<name>FAY KILMER</name>
<nFilms>20</nFilms>
</actor>
<actor id="153">
<name>MINNIE KILMER</name>
<nFilms>20</nFilms>
</actor>
</actors>