FLWOR — XQuery 的循环查询语言,以及九个实战场景¶
这一章讲什么: 前面各章的 XQuery 都是「点查」——一条路径取一个值。 FLWOR(读作 flower)是 XQuery 里唯一能「循环 + 变换」的结构: 遍历一堆节点、逐个过滤、排序、算出新值、构造出全新的 XML。 全书以此收官:它是把前 11 章所有零件(nodes、value、谓词、函数、命名空间) 拧成一个编程环境的那颗螺丝。
1. 五个子句,一张 SQL 对照表¶
FLWOR 是什么的缩写:
| 子句 | 干什么 | SQL 里的对应 |
|---|---|---|
| FOR | 遍历一个节点序列(每轮一个) | FROM/JOIN |
| LET | 把值绑给变量(每轮算一次) | 变量赋值/子查询 |
| WHERE | 按条件过滤 | WHERE |
| ORDER BY | 排序 | ORDER BY |
| RETURN | 每个幸存节点输出什么(可以是新构造的 XML) | SELECT |
要求两条:FOR 和 LET 至少有一个;RETURN 必须总在最后1。一个直觉差异:FOR 每轮重算,LET 绑一次——for $x in /a/b return $x 逐个节点走;let $c := count(/a/b) return $c 只算一次2。
本章主线例子: AdventureWorks 的 Sales.Store.Demographics 列(商店调查 XML,根元素 <StoreSurvey>,第 06 章拆过它)——这一章不换数据,九个场景全在它身上递进,正好检验前 11 章的积累。
最小完整示例(场景一:基本提取):
SELECT s.BusinessEntityID, s.Name,
s.Demographics.query('
declare default element namespace "<StoreSurvey 的命名空间 URI>";
for $store in /StoreSurvey
return
<StoreInfo>
<AnnualSales>{$store/AnnualSales}</AnnualSales>
<NumberEmployees>{$store/NumberEmployees}</NumberEmployees>
<Specialty>{$store/Specialty}</Specialty>
</StoreInfo>') AS StoreDetails
FROM Sales.Store s
WHERE s.Demographics IS NOT NULL;
三个要点:命名空间声明照旧必须;for $store in /StoreSurvey 让每个 StoreSurvey 元素轮一遍;return 里的花括号 {…} 是 XQuery 的「值拼接」语法——花括号内求值、花括号外当字面标签,所以能一边写死 <AnnualSales> 标签、一边塞入 $store/AnnualSales 的值3。
2. 九个场景:一条技能进阶线¶
场景二:WHERE 过滤。 找年销售额超百万的店。关键动作:先 LET 绑出数值再比——let $sales := xs:decimal($store/AnnualSales) where $sales > 1000000。xs:decimal() 是类型转换(XML 里的数字文本转成可比的十进制数);不转直接比文本,结果不可预期4。
场景三:ORDER BY 与多 LET。 按营业面积从大到小排。let $sqft := xs:integer($store/SquareFeet) + order by $sqft descending——ORDER BY 排在 RETURN 之前;多个 LET 串行绑定,像流水线的工序5。
场景四:if-then-else 分类。 按面积把店分成 Large/Medium/Small:LET 里嵌套条件——let $classification := if ($sqft >= 20000) then "Large" else if …——if-then-else 是表达式不是语句(07 章改 XML 时的同一军规),三档用嵌套表达6。
场景五:字符串函数。 string()(节点转文本)、upper-case()(转大写)、string-length()(数长度)——where $nameLength > 0 顺手把空值挡在门外7。
场景六:嵌套迭代与计算。 多个 LET 搭计算流水线:销售额 ÷ 员工数 = 人均产出、毛利 ÷ 销售额 = 利润率,round(x * 100) div 100 保留两位(注意 XQuery 的除法是 div 不是 /——/ 是路径符);除零用 WHERE 预先挡:where $sqft > 0 and $employees > 08。
场景七:动态构造。 按数据值决定元素出不出场:if (条件) then <元素>…</元素> else ()——() 是空序列,等于「什么都不输出」;属性也能动态拼:<元素 attr="{…}">9。
场景八:模式匹配过滤。 contains() 文本包含、配合 upper-case() 消除大小写差;castable as 先探后转:$store/YearOpened castable as xs:integer 为真才转,防转换炸错10。
场景九:综合对标报告。 把前面全部招式合在一支查询里:人均销售、每平方英尺营收、利润率三个衍生指标,多档评级(if-then-else 嵌套),按算出来的值排序,WHERE 全程守除零——这是全章的「毕业设计」11。
3. 最佳实践与排错(本章最实用的部分)¶
七条实践(书里逐条给了正反例):
- 永远显式声明命名空间(query() 字符串内 declare 或 declare default);
- 类型转换显式化——
let $sales := xs:decimal(...)好,裸比文本险; - 处理缺失值——先
exists()探存在,或castable as探可转; - 防除零——WHERE 里守卫,别裸
div; - 取节点用谓词——
($store/Specialty/text())[1]显式取第一个; - 选对方法——单值用
.value()、结构用.query()、存在性用.exist(),别什么都要 FLWOR; - 早过滤——
where xs:integer($x) > 50在源头筛,别 return 后再拿谓词捞12。
四个常见错误: XQst0005(命名空间不对或没声明——解法是核对 URI,书里教了一招:用 value('(/*/@*:*)[1]') 把文档里真实的命名空间读出来对比);XQty0018(漏了 RETURN);「Cannot convert cast value」(对非数字强转数字——用 castable as 守门);XQst0033(变量没声明就引用)13。
五步渐进式排错法(值得抄成肌肉记忆): ① 先确认数据在(SELECT TOP 1 看原文);② 用最简单的 .value() 取一个标量,验证命名空间和路径都对;③ 把命名空间加进简单 query();④ 先写 FOR+RETURN 骨架跑通;⑤ 逐步加 LET 和 WHERE——一次只加一层,炸在哪层一目了然14。
性能口径(书里克制但明确): 测 XML 之前先 COUNT(*) 看有多少行有数据;FLWOR 跑在 query() 里,前面学的 XML 索引(09/10 篇)照样帮它加速;看执行计划找优化点15。
4. 作者的判断与证据¶
结构性事实: 五个子句的语义、if-then-else 是表达式、div 除法、() 空序列——语法层面的内容,每个场景配了可跑代码和输出截图169。
经验性建议: 七条实践与五个排错步骤,都是「踩坑清单」式的经验浓缩——尤其「五步渐进式」和「把真实命名空间读出来对比」两条,属于只有教过别人才知道的教法1314。
收尾的元信息: 全书结语确认第二版的定位——补第一版的缺口、响应读者的点名需求(内建进程一章就是第一版读者问出来的),把 2000→2025 的 XML/JSON 演进线收拢成一份现代参考16。这句话佐证了本书拆解的一个判断:这本书是「SQL Server 半结构化数据」这一领域的操作手册,不是理论著作。
5. 边界与局限¶
- FLWOR 在 SQL Server 的 XQuery 里是子集:没有 XQuery 标准里的某些高级特性(如某些函数和序列操作),书里没正面列清单——写复杂逻辑前最好对着 SQL Server 的 XQuery 支持文档核对(补充,不在书里)。
- 性能上限:FLWOR 逐节点构造的输出,行数大时(几万行文档反复构造)成本可观——索引帮的是「找节点」,帮不了「构造输出」(补充判断,依 09 篇索引机制推得)。
- 和 T-SQL 的分工:复杂业务计算(多表关联、聚合报表)仍是 T-SQL 的主场;FLWOR 的甜区是「单文档内的遍历与变换」。
6. 可带走的¶
主线一行走完: StoreSurvey → FOR+RETURN 提取三字段(花括号拼接!)→ LET+WHERE+xs:decimal 过滤百万店 → 多 LET+ORDER BY 按面积降序 → if-then-else 三档分类 → string/upper-case/string-length 文本处理 → 嵌套 LET 算人均与利润率(round+div,WHERE 防除零)→ if/else () 动态构造元素 → contains+castable as 模式过滤 → 综合对标报告 → 七实践、四错误、五步法收尾。
- FLWOR 五子句:FOR/LET/WHERE/ORDER BY/RETURN;FOR 或 LET 至少其一,RETURN 收尾1。
- FOR 每轮重算,LET 绑一次2。
- RETURN 里花括号
{…}求值、外面是字面标签——构造 XML 的拼接语法3。 - 数字比较先
xs:decimal()/xs:integer()显式转换4。 - if-then-else 是表达式;空序列
()= 不输出该元素69。 - XQuery 除法是
div;除零用 WHERE 守卫8。 castable as先探后转,防转换炸错10。- 选对方法:单值 value()、结构 query()、存在 exist()——不是什么都上 FLWOR12。
- 四个错误号背俩:XQst0005(命名空间)、XQty0018(漏 RETURN);排错第一招是把文档真实命名空间读出来对比13。
- 五步渐进式:验证数据 → 单值 value() → 加命名空间 → FOR+RETURN 骨架 → 逐步加 LET/WHERE14。
7. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| FLWOR 五子句与对照表 | Chapter 12: Using FLWOR Expressions in SQL Server | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:26(搜「Clause」) |
| FOR vs LET 对照 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:64(搜「Re-evaluates」) |
| 场景一与花括号构造 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:97(搜「for $store in」) |
| 场景二与类型转换 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:142(搜「xs:decimal()」) |
| 场景三与排序 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:161(搜「order by $sqft descending」) |
| 场景四与 if-then-else | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:194(搜「let $classification」) |
| 场景五与字符串函数 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:248(搜「String Functions Used」) |
| 场景六与计算管线 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:276(搜「Stage calculations」) |
| 场景七与动态构造 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:300(搜「Conditional elements」) |
| 场景八与 castable as | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:323(搜「castable as」) |
| 七条实践 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:364(搜「Always Declare Namespaces」) |
| 四个错误与解法 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:457(搜「Common Errors」) |
| 五步渐进式 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:488(搜「Step 1」) |
| 性能与全书小结 | 同上 | text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:445(搜「Performance Considerations」) · text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:584(搜「Summary of XML and JSON」) |
-
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 25–33 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:26,搜「Clause」)。五子句对照表:FOR「Iterate over a sequence (node list)」对应 FROM/JOIN,LET「Bind a value to a variable」对应变量赋值/子查询,WHERE 对应 WHERE,ORDER BY 对应 ORDER BY,RETURN 对应 SELECT;「At least one of FOR or LET」「Required」两栏标出硬性要求。 ↩↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 62–70 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:64,搜「Re-evaluates」)。原文:「FOR: Re-evaluates for each item in sequence」「LET: Evaluates expression once and reuses the result」,示例for $item in /items/item return $item/name与let $count := count(/items/item) return $count并列。 ↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 89–113 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:97,搜「for $store in」)。Listing 12-1:namespace 声明、「FOR clause: Iterates through the StoreSurvey element」「RETURN clause: Constructs new XML with extracted values」。 ↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 114–144 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:142,搜「xs:decimal()」)。原文:「xs:decimal(): Type casting for numeric comparison」「LET clause: Extracts and stores the numeric value for filtering」。 ↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 145–178 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:161,搜「order by $sqft descending」)。多 LET 与「Stage-based value extraction」「Order by descending: Reverse sort order」。 ↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 180–215 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:194,搜「let $classification」)。三档嵌套:if ($sqft >= 20000) then "Large" else if ($sqft >= 10000) then "Medium" else "Small";「Nested conditional expressions」「Tiered classification logic」。 ↩↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 216–251 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:248,搜「String Functions Used」)。原文:「string(): Converts nodes to text」「upper-case(): Converts to uppercase」「string-length(): Gets character count」。 ↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 253–279 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:276,搜「Stage calculations」)。原文:「Multiple LET clauses: Stage calculations」「Conditional calculations: Avoid division by zero」「round() and div: Numeric rounding and division」。 ↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 281–303 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:300,搜「Conditional elements」)。原文:「Conditional elements: Elements only appear when the condition is true」「Empty sequence (): Omits elements when conditions fail」「Attribute construction: Dynamic attributes based on data」。 ↩↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 305–324 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:323,搜「castable as」)。原文:「castable as: Check if the value can be cast before conversion」「Complex WHERE conditions: Multiple OR conditions」。 ↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 325–362 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:357,搜「Comprehensive Analysis Techniques」)。原文:「Multiple calculated metrics: Stage-based calculations」「Performance rating logic: Multi-tier classification」「Complete data validation: WHERE ensures safe division」。 ↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 364–444 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:364,搜「Always Declare Namespaces」)。七条逐条列示,含正反例:「For single values, consider .value()」「For structured XML, use .query()」「For checking existence, use .exist()」;「Good: Filters at source…Less efficient: Processes all then filters」。 ↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 457–485 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:457,搜「Common Errors」)。四错误:XQst0005(命名空间;解法含value('(/*/@*:*)[1]', 'NVARCHAR(MAX)')读真实命名空间)、XQty0018(缺 RETURN)、Cannot convert cast value(castable as 守门)、XQst0033(变量未声明)。 ↩↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 487–525 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:488,搜「Step 1」)。五步:验证数据存在 → 简单 .value() → 命名空间加进简单 query() → FOR+RETURN 骨架 → 逐步加 LET/WHERE。 ↩↩↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 445–456 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:447,搜「Verify XML Data Exists」)与第 566–578 段(text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:572,搜「XML indexes」)。原文:「Create XML indexes to improve query performance」「Monitor execution plans to identify optimization opportunities」。 ↩ -
出处:「Chapter 12: Using FLWOR Expressions in SQL Server」第 584–618 段(
text/16-ch12-chapter-12-using-flwor-expressions-in-sql-server.txt:585,搜「up-to-date」)。第二版小结:「filling gaps from the first edition and addressing topics readers specifically requested…a complete, modern reference for developers, DBAs, and data professionals」。 ↩