编程语言研究书库

FLWOR — XQuery 的循环查询语言,以及九个实战场景

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. 最佳实践与排错(本章最实用的部分)

七条实践(书里逐条给了正反例):

  1. 永远显式声明命名空间(query() 字符串内 declare 或 declare default);
  2. 类型转换显式化——let $sales := xs:decimal(...) 好,裸比文本险;
  3. 处理缺失值——先 exists() 探存在,或 castable as 探可转;
  4. 防除零——WHERE 里守卫,别裸 div;
  5. 取节点用谓词——($store/Specialty/text())[1] 显式取第一个;
  6. 选对方法——单值用 .value()、结构用 .query()、存在性用 .exist(),别什么都要 FLWOR;
  7. 早过滤——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 模式过滤 → 综合对标报告 → 七实践、四错误、五步法收尾。

  1. FLWOR 五子句:FOR/LET/WHERE/ORDER BY/RETURN;FOR 或 LET 至少其一,RETURN 收尾1。
  2. FOR 每轮重算,LET 绑一次2。
  3. RETURN 里花括号 {…} 求值、外面是字面标签——构造 XML 的拼接语法3。
  4. 数字比较先 xs:decimal()/xs:integer() 显式转换4。
  5. if-then-else 是表达式;空序列 () = 不输出该元素69。
  6. XQuery 除法是 div;除零用 WHERE 守卫8。
  7. castable as 先探后转,防转换炸错10。
  8. 选对方法:单值 value()、结构 query()、存在 exist()——不是什么都上 FLWOR12。
  9. 四个错误号背俩:XQst0005(命名空间)、XQty0018(漏 RETURN);排错第一招是把文档真实命名空间读出来对比13。
  10. 五步渐进式:验证数据 → 单值 value() → 加命名空间 → FOR+RETURN 骨架 → 逐步加 LET/WHERE14。

7. 原文地图

主题原书章原文位置
FLWOR 五子句与对照表Chapter 12: Using FLWOR Expressions in SQL Servertext/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」)

Footnotes

  1. 出处:「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」两栏标出硬性要求。 ↩ ↩2 ↩3

  2. 出处:「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 并列。 ↩ ↩2

  3. 出处:「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」。 ↩ ↩2

  4. 出处:「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」。 ↩ ↩2

  5. 出处:「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」。 ↩

  6. 出处:「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」。 ↩ ↩2 ↩3

  7. 出处:「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」。 ↩

  8. 出处:「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」。 ↩ ↩2

  9. 出处:「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」。 ↩ ↩2 ↩3

  10. 出处:「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」。 ↩ ↩2

  11. 出处:「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」。 ↩

  12. 出处:「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」。 ↩ ↩2

  13. 出处:「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(变量未声明)。 ↩ ↩2 ↩3

  14. 出处:「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。 ↩ ↩2 ↩3

  15. 出处:「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」。 ↩

  16. 出处:「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」。 ↩

On this page