XQuery FLWOR 表达式最全详解(2025 版)
FLWOR 是 XQuery 最核心、最常用的表达式,几乎 90% 的实际查询都会用到它。
它读作 “flower”,是以下 5 个子句的首字母缩写:
| 子句 | 全称 | 作用 | 是否必须 |
|---|---|---|---|
| F | for | 迭代序列(生成变量绑定) | 可选 |
| L | let | 绑定变量(不迭代,只绑定一次) | 可选 |
| W | where | 过滤条件 | 可选 |
| O | order by | 排序 | 可选 |
| R | return | 构造最终返回结果 | 必须 |
至少要有 for/let 其中一个 + return 才构成合法 FLWOR。
1. 基本语法模板(背下来就赢了)
for $变量1 in 表达式1
[, $变量2 in 表达式2 ...]
let $变量3 := 表达式3
[, $变量4 := 表达式4 ...]
where 条件表达式
order by 排序表达式 [ascending|descending] [collation "..."]?
return 结果表达式
2. 每个子句深度解析 + 实战例子
使用以下 books.xml 作为全程示例:
<bookstore>
<book category="web" year="2020"><title>XQuery 实战</title><price>88.00</price></book>
<book category="web" year="2003"><title>XQuery Kick Start</title><price>49.99</price></book>
<book category="cooking" year="2005"><title>Italian Cooking</title><price>30.00</price></book>
<book category="children" year="2005"><title>Harry Potter</title><price>29.99</price></book>
</bookstore>
| 子句 | 例子 | 说明 |
|---|---|---|
| for | for $b in doc("books.xml")//book | 每次迭代把每一本书绑定到 $b,最常用 |
| 多 for | for $b in //book, $a in $b/author | 笛卡尔积(交叉连接),常用于多集合 join |
| let | let $books := doc("books.xml")//book | 只绑定一次,常用于避免重复计算 |
| let + for | for $b in //book let $discount := $b/price * 0.9 | 每一本书都有自己的 $discount |
| where | where $b/price > 40 and $b/@category = "web" | 过滤,性能比后置过滤高很多 |
| 多条件 | where $b/year ge 2005 and contains($b/title, "XQuery") | 支持 and / or / not() |
| order by | order by xs:decimal($b/price) descending, $b/year ascending | 支持多字段排序 |
| 动态排序 | order by $b/(if ($sort = 'year') then year else price) | 常见于 REST API 参数排序 |
| return | return <item>{ $b/title, <p>{ $b/price }</p> }</item> | 可构造任意 XML、文本、序列 |
3. 经典完整例子(直接复制到 BaseX 运行)
(: 1. 最经典的 FLWOR:价格 > 40 的 web 类书籍,按价格降序,输出 JSON 风格 :)
for $book in doc("books.xml")//book
where $book/price > 40
and $book/@category = "web"
order by xs:decimal($book/price) descending
return
<result>{
$book/title,
$book/price,
<year>{ $book/@year }</year>
}</result>
(: 2. let 的经典用法:分组统计(类似 SQL GROUP BY):)
for $cat in distinct-values(doc("books.xml")//book/@category)
let $books := doc("books.xml")//book[@category = $cat]
let $count := count($books)
let $avg := avg($books/price)
where $count > 1
order by $avg descending
return
<category name="{$cat}" count="{$count}" avg-price="{$avg}"/>
(: 3. 多 for 实现 join(两个 XML 文件):)
for $book in doc("books.xml")//book
$review in doc("reviews.xml")//review[@book-id = $book/@id]
where $review/rating > 4
return
<good-book title="{$book/title}" rating="{$review/rating}"/>
(: 4. 带序号的 FLWOR(常见需求:返回第 1–10 条):)
for $book at $pos in doc("books.xml")//book
where $pos >= 1 and $pos <= 10
order by $book/price descending
return
<row rank="{$pos}">{ $book/title, $book/price }</row>
4. FLWOR vs 纯 XPath 路径表达式对比
| 需求 | 纯 XPath(简单但能力有限) | FLWOR(强大灵活)推荐写法 |
|---|---|---|
| 价格 > 30 的书 | //book[price > 30] | for $b in //book where $b/price > 30 return $b |
| 排序 + 构造新结构 | 做不到 | 必须用 FLWOR |
| 分组统计 | 做不到 | 必须 let + for |
| join 两个文档 | 做不到 | 多 for 或 let + for |
| 分页(带序号) | 做不到 | 用 at $pos |
5. 性能技巧(生产必知)
- where 越早过滤越好(不要在 return 里再过滤)
- let 绑定大集合时小心内存(能 for 就用 for)
- order by 尽量只排一个字段(多字段排序代价高)
- 使用索引(BaseX、eXist-db、MarkLogic 都支持):
for $b in db:open("mydb")//book[price > 100]
6. 一句话总结
只要你会写 FLWOR,就等于掌握了 95% 的 XQuery 实战能力
背熟上面 4 个经典模板,任何复杂查询都能 1 分钟内写出来!
需要我给你出 20 道 FLWOR 练习题 + 答案吗?可以立刻发~