XQuery FLWOR
FLWOR is the heart of XQuery. It stands for For, Let, Where, Order by, Return — five clauses that combine to form a complete query expression. FLWOR is to XQuery what SELECT-FROM-WHERE-ORDER BY is to SQL. Once you understand FLWOR, you can write complex XQuery queries that filter, sort, group, and reshape XML data.
FLWOR Structure
for $variable in expression ← iterate over a sequence let $variable := expression ← bind a value to a variable where condition ← filter results order by expression ← sort results return expression ← define the output for each iteration
Only for and return are required. The others are optional.
Working XML File: employees.xml
<company>
<employee dept="Engineering">
<name>Arjun Kumar</name><salary>85000</salary><experience>5</experience>
</employee>
<employee dept="Design">
<name>Meena Pillai</name><salary>72000</salary><experience>3</experience>
</employee>
<employee dept="Engineering">
<name>Ravi Sharma</name><salary>91000</salary><experience>8</experience>
</employee>
<employee dept="HR">
<name>Sunita Joshi</name><salary>58000</salary><experience>2</experience>
</employee>
</company>
The for Clause
The for clause binds each node in a sequence to a variable, one at a time. The dollar sign $ marks a variable in XQuery.
for $emp in doc("employees.xml")/company/employee
return $emp/name
Output
<name>Arjun Kumar</name> <name>Meena Pillai</name> <name>Ravi Sharma</name> <name>Sunita Joshi</name>
The where Clause — Filtering
The where clause filters iterations — only those where the condition is true produce output.
for $emp in doc("employees.xml")/company/employee
where $emp/salary > 75000
return $emp/name
Output — only high earners
<name>Arjun Kumar</name> <name>Ravi Sharma</name>
The order by Clause — Sorting
The order by clause sorts results before returning them. Add descending for reverse order; default is ascending.
for $emp in doc("employees.xml")/company/employee
order by $emp/salary descending
return
<result>
<name>{ $emp/name/text() }</name>
<salary>{ $emp/salary/text() }</salary>
</result>
Output — sorted highest salary first
<result><name>Ravi Sharma</name><salary>91000</salary></result> <result><name>Arjun Kumar</name><salary>85000</salary></result> <result><name>Meena Pillai</name><salary>72000</salary></result> <result><name>Sunita Joshi</name><salary>58000</salary></result>
The let Clause — Variables
The let clause assigns a value or sequence to a variable for use later in the query. Unlike for, let does not create a loop — it binds the whole sequence at once.
let $allEmployees := doc("employees.xml")/company/employee
let $avgSalary := avg($allEmployees/salary)
return
<summary>
<totalEmployees>{ count($allEmployees) }</totalEmployees>
<averageSalary>{ $avgSalary }</averageSalary>
</summary>
Output
<summary> <totalEmployees>4</totalEmployees> <averageSalary>76500</averageSalary> </summary>
Complete FLWOR with All Clauses
A query that finds Engineering employees, calculates a bonus, and returns sorted results:
let $bonus-rate := 0.15
for $emp in doc("employees.xml")/company/employee
where $emp/@dept = "Engineering"
order by $emp/salary descending
return
<engineerReport>
<name>{ $emp/name/text() }</name>
<salary>{ $emp/salary/text() }</salary>
<bonus>{ $emp/salary * $bonus-rate }</bonus>
<years>{ $emp/experience/text() }</years>
</engineerReport>
Output
<engineerReport> <name>Ravi Sharma</name> <salary>91000</salary> <bonus>13650</bonus> <years>8</years> </engineerReport> <engineerReport> <name>Arjun Kumar</name> <salary>85000</salary> <bonus>12750</bonus> <years>5</years> </engineerReport>
FLWOR vs SQL Comparison
| FLWOR Clause | SQL Equivalent | Purpose |
|---|---|---|
| for | FROM | Identifies the data source (iterates nodes) |
| let | (no direct equivalent) | Binds variables for reuse |
| where | WHERE | Filters results by condition |
| order by | ORDER BY | Sorts the output |
| return | SELECT | Defines what the output looks like |
Nested FLWOR Expressions
FLWOR expressions can nest inside each other to handle hierarchical data.
for $dept in distinct-values(doc("employees.xml")/company/employee/@dept)
return
<department name="{ $dept }">
{
for $emp in doc("employees.xml")/company/employee[@dept = $dept]
order by $emp/name
return <member>{ $emp/name/text() }</member>
}
</department>
Output
<department name="Design"> <member>Meena Pillai</member> </department> <department name="Engineering"> <member>Arjun Kumar</member> <member>Ravi Sharma</member> </department> <department name="HR"> <member>Sunita Joshi</member> </department>
Key Points to Remember
- FLWOR stands for For, Let, Where, Order by, Return.
- for and return are the only required clauses.
- for iterates over a sequence; let binds a whole sequence to a variable at once.
- where filters — only matching iterations produce output.
- order by sorts the results; add descending for reverse order.
- return defines the output structure — it can contain XML literals with {} expressions.
- FLWOR expressions can nest to handle multi-level XML hierarchies.
