Chapter 15: XML XQuery
1. What is XQuery? (The clearest honest explanation)
XQuery = XML Query Language (pronounced “X-query”)
It is a standard query language designed specifically to:
- Search
- Extract
- Transform
- Combine
- Calculate over … one or more XML documents
Think of XQuery as SQL for XML — but more powerful and more flexible.
While XPath is mainly used to select nodes (like a path expression),
XQuery is a full programming language that can:
- do loops
- make conditions
- define variables & functions
- perform calculations
- create new XML structures
- join multiple documents
- sort results
- group data … and much more
Analogy most people understand quickly:
- XPath = the address on an envelope (“go to library → book → title”)
- XQuery = the full letter you write (“go to the library, find all books published after 2000, sort them by title, and make me a nice table with title + author + price”)
2. XPath vs XQuery – Quick Comparison Table (very important!)
| Feature | XPath | XQuery |
|---|---|---|
| Main purpose | Select nodes / paths | Query + transform + construct new XML |
| Can create new elements? | No | Yes |
| Loops (for / let) | No (only limited predicates) | Yes |
| Variables | Very limited | Yes (let $var := …) |
| Functions & user-defined funcs | No | Yes |
| Sorting | Very limited | Yes (order by) |
| Grouping | No | Yes (group by in XQuery 3.0+) |
| Joins between documents | Hard / limited | Easy |
| FLWOR expression | No | Yes (for-let-where-order-return) |
| Typical use | Inside XSLT, DOM, configuration | Reporting, data integration, XML databases |
Rule of thumb people use in 2025–2026:
- Need only to find / select nodes → XPath
- Need to build new structure, calculate, join, sort, group → XQuery
3. Very First Simple Example – Selecting & Formatting
Input XML (books.xml)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<library> <book year="2018" price="499.00" currency="INR"> <title>Atomic Habits</title> <author>James Clear</author> </book> <book year="1997" price="350.00" currency="INR"> <title>Rich Dad Poor Dad</title> <author>Robert Kiyosaki</author> </book> <book year="2003" price="12.99" currency="USD"> <title>The Alchemist</title> <author>Paulo Coelho</author> </book> </library> |
Very simple XQuery (select all titles)
for $book in /library/book
return $book/titleOutput (sequence of text nodes):
|
0 1 2 3 4 5 6 7 8 |
Atomic Habits Rich Dad Poor Dad The Alchemist |
Slightly nicer version (with formatting)
for $book in /library/book
return
<li>{$book/title/string()} by {$book/author/string()}</li>Result:
|
0 1 2 3 4 5 6 7 8 |
<li>Atomic Habits by James Clear</li> <li>Rich Dad Poor Dad by Robert Kiyosaki</li> <li>The Alchemist by Paulo Coelho</li> |
4. The Heart of XQuery: FLWOR Expression
FLWOR = For – Let – Where – Order by – Return (the most common pattern in real XQuery code)
Example – books after 2000, sorted by title, with calculated discount
for $book in /library/book
let $price := number($book/@price)
let $discounted := $price * 0.85
where $book/@year >= 2000
order by $book/title ascending
return
<book-result>
<title>{$book/title/string()}</title>
<author>{$book/author/string()}</author>
<original-price>{$price}</original-price>
<discounted-price>{round($discounted, 2)}</discounted-price>
</book-result>Output:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<book-result> <title>Atomic Habits</title> <author>James Clear</author> <original-price>499</original-price> <discounted-price>424.15</discounted-price> </book-result> <book-result> <title>The Alchemist</title> <author>Paulo Coelho</author> <original-price>12.99</original-price> <discounted-price>11.04</discounted-price> </book-result> |
5. More Realistic Example – Report from Order Data
Input XML (orders.xml)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<orders> <order id="ORD-202507-4521" date="2025-07-20" customer="CUST-784"> <item sku="LAP-001" qty="1" price="145000.00"/> <item sku="MOUSE-01" qty="2" price="1499.00"/> </order> <order id="ORD-202507-4522" date="2025-07-21" customer="CUST-129"> <item sku="PHONE-15" qty="1" price="79999.00"/> </order> </orders> |
XQuery – Generate summary report
declare variable $orders := doc("orders.xml")/orders;
<summary-report generated="{current-date()}">
{
for $order in $orders/order
let $total := sum($order/item/(@qty * @price))
let $item-count := count($order/item)
where $total > 50000
order by $total descending
return
<order-summary>
<order-id>{$order/@id/string()}</order-id>
<date>{$order/@date/string()}</date>
<item-count>{$item-count}</item-count>
<grand-total currency="INR">{round($total, 2)}</grand-total>
<high-value>{"YES" if ($total > 100000) else "NO"}</high-value>
</order-summary>
}
</summary-report>
Output:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<summary-report generated="2025-07-20"> <order-summary> <order-id>ORD-202507-4521</order-id> <date>2025-07-20</date> <item-count>2</item-count> <grand-total currency="INR">147998</grand-total> <high-value>YES</high-value> </order-summary> <order-summary> <order-id>ORD-202507-4522</order-id> <date>2025-07-21</date> <item-count>1</item-count> <grand-total currency="INR">79999</grand-total> <high-value>NO</high-value> </order-summary> </summary-report> |
6. Very Common Real-World Patterns
| Use case | Typical XQuery pattern |
|---|---|
| Generate HTML report | Return <html><table>{for $row …}</table></html> |
| Create JSON (XQuery 3.0+) | json:serialize( map { … } ) |
| Join two documents | for $o in doc(“orders”)//order, $c in doc(“customers”)//customer where $o/@customer = $c/@id return … |
| Group by category (XQuery 3.0) | for $cat in distinct-values(//category) group by $cat return … |
| Filter + calculate | let $total := sum(//item/(@qty * @price)) |
| Transform XML to different namespace | Use constructor + namespace declarations |
Quick Summary – XQuery Cheat Sheet
| Goal | Typical expression |
|---|---|
| Loop over elements | for $x in //book |
| Define variable | let $total := sum(//price) |
| Filter | where $x/@year > 2000 |
| Sort | order by $x/title ascending |
| Build new XML | <result><title>{$x/title}</title></result> |
| Get text value | $x/title/string() or data($x/title) |
| Count items | count(//item) |
| Sum / avg / min / max | sum(//price), avg(//price) |
Would you like to continue with one of these topics?
- XQuery 3.0 / 3.1 features (group by, arrays, maps, JSON support)
- Joining multiple XML files (very common in real projects)
- How to run XQuery (tools, BaseX, Saxon, Zorba, eXist-db, command line)
- XQuery inside XSLT (yes – you can mix them)
- XQuery vs SQL/XML vs XSLT – when to choose what
- Real example from e-invoice / EDI / healthcare / catalog data
Just tell me which direction feels most useful or interesting for you right now! 😊
