XQuery(An XML Query Language) 是一种专门用于XML的半结构化数据查询语言。这是一篇XQuery的笔记。

概述

XQuery的功能与XSLT类似,但是写起来较为直观简便。

语法

一个XQuery文档的基本框架为:

1
2
3
for $变量名 in doc("xml-location")/xpath
...
return ...

其含义即为打开 doc() 中指定的xml文档,并通过xpath路径找到所有结点,绑定到变量上。而执行完一系列操作语句,最后return的语句即为输出的内容。

FLOWR表达式

XQuery有五种最基础的表达式,分别为 for let order by where return ,简称为FLOWR。

for

for语句对应于XSLT中的 <xsl:for-each> ,其最基础的用法为 for $x in expr ,其中expr可以是xpath表达式也可以是一串序列,这时for语句会依次读取其中的值:

1
2
3
for $x in ("cat","dog")
return $x
//输出 cat dog

也可以在for中定义多个变量,这时输出的时候就会以其组合形式输出:

1
2
3
for $i in (1,2), $j in ("cat","dog")
return ($i,$j)
//输出 1 cat 1 dog 2 cat 2 dog

for语句中还可以借助 at 进行迭代,其语法为 for $变量名 at $迭代名 in expr

1
2
3
for $j at $i in ("cat","dog")
return ($i,$j)
//输出 1 cat 2 dog

排序模式

在XQuery中,有ordered和unordered两种排序模式。在unordered模式下,for子句中声明的范围变量的循环绑定次序不一定是文档序。不同的XQuery引擎会采取相应的优化机制进行绑定,从而提高查询效率。例:

1
2
3
4
5
6
unordered {
for $p in fn:doc("parts.xml")/parts/part[color="red"]
for $s in fn:doc("suppliers.xml")/supplies/supplier
where $p/sno = $s/sno
return <ps> { $p/pno, $s/sno } </ps>
}

let

let语句用来声明 局部变量 ,基本语法为 let $value-name as type := value ,并且不会触发迭代:

1
2
3
let $x:=(1 to 5),$y:=<y>{$x}</y>
return ($x,$y)
//输出 1 2 3 4 5<y>12345</y>

order by

order by语句用于指定结果的次序,必须 与for搭配使用,其基本结构为 (stable) order by $排序变量 [ascending | descending] ,其中 stable 用于设置排序是否稳定,而 ascending | descending 则是指定其为升序或降序,默认为升序。还是上面那个文档:

1
2
3
4
for $x in doc("book.xml")/books/book
order by $x/price
return $x/title
//输出 <title>Cpp</title><title>Cs</title>
1
2
3
4
for $x in doc("book.xml")/books/book
order by $x/price descending
return $x/title
//输出 <title>Cs</title><title>Cpp</title>

强制转换

对于排序目标,我们可以使用 cast as 语句对其进行强制类型转换,例:order by $student/NO cast as xs:int

where

where的用法与XSLT中的 <xsl:if> 类似,用于添加判断条件:where expr 。举个例子:在XML文档book.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book>
<title>Cpp</title>
<author>thq</author>
<price>30</price>
<price>60</price>
</book>
<book>
<title>Cs</title>
<author>THU</author>
<price>50</price>
<price>60</price>
</book>
</books>
1
2
3
4
for $x in doc("book.xml")/books/book
where $x/price > 40
return $x/title
//输出 <title>Cs</title>

在where语句中,我们还可以使用 量词表达式 ,其基本语法为 where (some | every) $变量 in 位置 satisfies expr ,some与every为可选词,分别意为 存在任意 ,还是对上面那个文档举个例子:

1
2
3
4
for $x in doc("book.xml")/books/book
where some $author in $x/author satisfies $author="THU"
return $x/title
//输出 <title>Cpp</title><title>Cs</title>
1
2
3
4
for $x in doc("book.xml")/books/book
where every $author in $x/author satisfies $author="THU"
return $x/title
//输出 <title>Cs</title>

return

return语句,从上面的例子中应该能看出来要怎么用、有什么用了吧!

构造方法

在输出的结果中,我们经常需要构造出XML元素或属性来保存数据,一般来说,有直接构造与计算构造两种构造方式:

直接构造

直接构造,顾名思义就是像前文的几个例子中一般静态地构造,还是用上面那个XML文档的例子,来构造一段元素与属性:

1
2
3
for $x in doc("book.xml")/books/book
return <book price="{$x/price}">{data($x/title)}</book>
//输出 <book price="30">Cpp</book><book price="50">Cs</book>

注意:data() 函数可以获取结点的值

if表达式

XQuery也支持if表达式的筛选: if ... then ... else ...

计算构造

计算构造的方法比较类似于XSLT中的 <xsl:element><xsl:attribute> ,能够更为自由地构造

构造元素

构造元素的基本框架为 element {元素名} {元素内容} ,举个例子:

1
2
3
for $x in doc("book.xml")/books/book
return element{concat("book-",node-name($x/title))} {data($x/title)}
//输出 <book-title>Cpp</book-title><book-title>Cs</book-title>

注意: concat() 函数用来连接多个字符串; node-name() 函数用来获取结点的名字

构造属性

构造属性的基本框架为 element {元素名} {attribute {属性名} {属性值} , {元素内容} } 例子:

1
2
3
for $x in doc("book.xml")/books/book
return element book {attribute price {data($x/price)}, data($x/title)}
//输出 <book price="30">Cpp</book><book price="50">Cs</book>
构造其他内容

文本: text {}

注释: comment {}

处理指令: processing-instruction {} {}

1
2
3
for $x in doc("book.xml")
return (text {"TEXT"}, comment {"COMMENT"}, processing-instruction name {"NAME"})
//输出 TEXT<!--COMMENT--><?name NAME?>

自定义函数

XQuery允许我们自定义函数,其基本框架为 declare function 命名空间:函数名 (参数列表) as 返回值类型 {函数体}

1
2
3
4
5
6
declare function local:func($price as xs:int) as xs:int{
($price div 10)
};
for $x in doc("book.xml")/books/book
return local:func($x/price)
//输出 3 5

模块

可以使用 module namespace 模块名 来声明一个模块,并使用 import module namespace 模块名 来进行调用

命名空间

可以使用 declare namespace 命名空间名 来声明一个命名空间