更新XQuery XQuery有了一些新特性内容包括从原子化到跟蹤文件结构 在你所不了解的XQuery(Oracle杂志年/月刊)一文中我介绍了XQuery它是一项由万维网联盟(WC)开发的技术设计用来查询和操纵XML数据或任何能以XML形式出现的数据如关系型数据库那篇引文讨论了年月发布的XQuery草案规范年月WC发布了新的XQuery草案规范本文追蹤报道了月份发布的草案规范中最令人感兴趣的变化和新增加的特性其中包括库模块序(prolog)变量外部函数以及用于调试错误处理和格式化的新函数 变化 月草案增加了大量新特性但是我首先讨论对现有特性所做的更改有些更改是表面上的 例如document()输入函数(该函数使用给定的统一资源标识符[URI]返回一个文档)被改为一个新的更短的名字doc()另外曾经被写成{ comment }的注释现在改用新的笑脸符号(: comment :)是的现在每个注释都成了一个笑话 有些更改则是更根本的也许最重大的改动就是distinctvalues()函数不再返回节点(节点是XML结构如元素文档注释以及文本节点)它只返回原子值(如整数或字符串)尽管该函数仍然接受节点和原子值但只返回原子值任何进入该函数的节点都会被原子化并被当作原子值然后以原子形式返回 原子化的规则很复杂这里给出一些基本的由模式定义为布尔型的元素将被原子化为true/false布尔值定义为整型的元素将被原子化为一个整数没有被模式定义的元素将被原子化为节点的XPath字符串值(文本节点递归地连接在一起) 为了说明 distinctvalues(<item>apple</item> <item>banana</item> grape) 返回值(applebananagrape)假设在模式中没有声明 在下面的例子中如果我们假设 被模式定义为布尔型那么下面的语句 distinctvalues(<status></status> <status>false</status>) 返回false()因为它是两个元素的原子值记住false()是XQuery常量表示假 现在你也许会想当我想返回节点时可以使用distinctnodes()函数 是的但该函数只能根据节点标识删除重复节点(那些完全相同的节点类似于Java中引用的等效节点)没有能删除等效节点的函数这会使查询变得复杂因为没有办法能轻松地删除等效节点 回过头来看我以前的那篇文章你所不了解的XQuery你将发现有些示例会受到这一改动的影响在那篇文章中下面的查询返回了艺术家名字的惟一列表其中每一个名字前后都带有 标记 distinctvalues(document(itunesxml) /itunes/Tracks/Track/Artist) 示例输出类似于 <Artist>Marc Cohn</Artist> <Artist>Pink Floyd</Artist> 现在执行同样的查询则返回原子值 Marc Cohn Pink Floyd 由于distinctvalues()去掉了 标记(这是原子化过程的一部分)所以你必须在完成distinctvalues()调用后添加标记如下所示 let $artists := distinctvalues(doc(itunesxml) /itunes/Tracks/Track/Artist) for $a in $artists return <Artist>{ $a }</Artist> 不是每种情况都是这么轻松地得到处理看一下WC 使用案例文档中的示例在年月版与年月版中是如何变化的该示例返回每位作者的着作列表它使用了distinctvalues()根据年月的规范它的代码如下 <results> { for $a in distinctvalues( document() //author) return <result> { $a } { for $b in document( ) /bib/book where some $ba in $b/author satisfies deepequal($ba$a) ;return $b/title } </result> } </results> 根据查询结果你不能直接分辨出姓和名但每个 元素都由一个 和 名组成distinctvalues()调用返回具有惟一名字的 元素列表对于年月的规范现在查询必须在姓和名上单独运行distinctvalues()而且在嵌套的FLWOR表达式中也没有将$a指定为惟一的作者 <results> { let $a := doc() //author for $last in distinctvalues($a/last) $first in distinctvalues( $a[last=$last]/first) return <result> { $last $first } { for $b in doc() /bib/book where some $ba in $b/author satisfies ($ba/last = $last and $ba/first=$first) return $b/title } </result> } </results> 除了编写用户定义的distinctdeepequal()外没有更好的方法来完成这件事了而该方法在纯XQuery中不能执行(注FLWOR(发音为flower)表达式是XQuery的构建模块这个名字来源于组成表达式的关键词ForLetWhereOrder by和Return} 新函数 年月的XQuery规范草案增加了三个新函数它们肯定会非常有用第一个是 trace($value as item()* $label as xs:string) as item()* trace()函数允许在查询的中间进行printf风格的调试该函数有两个参数要显示的值(可以是任意多个项的序列)以及要显示的这个值的字符串标签为方便起见函数返回$value传递的值trace()输出的位置由你的引擎来决定 该函数使你能够详细查看查询的内部过程例如下面的查询根据文档名返回在XQuery引擎中存储的所有文档的URI通过增加一个trace()调用我能够在排序前查看返回的每个URI define function uris() as xs:string* { for $n in input() return trace( xs:string(documenturi($n)) base:) } for $u in uris() order by $u return $u 输出结果可能如下 :: base: censusxml :: base: ipoxml 当使用trace()和其他类似的函数时记住在XQuery中每项内容都是一个表达式没有语句!为了能够完成类似语句的操作你可以采用在表达式之间加逗号的方法来创建一个序列然后单独计算每个表达式的值在最终的结果序列中忽略所有返回空值的表达式例如下面是两个不影响结果的trace()调用 trace(() starting query) let $time := currentdateTime() let $ignored := trace($time Got time) return <html> <head></head> <body>Current time is { $time }</body> </html> 请注意第一个trace()调用后面的逗号它使查询返回一个具有两个项的序列第一个trace()调用的结果为空因而被忽略一般人不了解这一情况但高级查询实际上就是返回一个序列因此在这种特殊情况下两端的括号不是必需的查询 是完全正确的此外在这个示例中你会看到当编写一个FLWOR表达式时你可以执行let子句右侧的任意代码并且忽略该值 月份的草案还增加了error()函数 error($srcval as item()?) 这个函数使用户能够报告一个错误类似于抛出一个异常$srcval被定义为item()?意味着它可以是一个XML结构或原子并且加上问号标记表示这是可选的下面是一些示例应用 error() error(Missing source document) error(<span>A <i>beautifully</i> formatted error</span>) error()调用就像发生异常时那样展开堆栈遗憾的是XQuery仍没有try/catch功能因此尽管你能抛出错误但却不能从中恢复 在月份的草案中最后一个引人注目的新增函数有一个奇怪的名字roundhalftoeven()它有两种形式 roundhalftoeven($srcval as numeric?) as numeric? roundhalftoeven($srcval as numeric? $precision as xs:integer) as numeric? 在有一个参数的情况下它的行为类似于round()函数只是当一个数恰好落在其他两个数的中间时它将参数取整为最接近的偶数值数字理论家们会告诉你从统计学上讲这是一个更精确的取整算法举例说明 roundhalftoeven() = roundhalftoeven() = roundhalftoeven() = 有第二个参数的情况使函数变得很有趣第二个参数表示精确级并允许将函数用于格式化小数值例如 roundhalftoeven( ) = roundhalftoeven( ) = roundhalftoeven( div ) = 在声明中使用的数字数据类型是xs:decimalxs:integerxs:floatxs:double以及任何根据限制由它们导出的类型的一种简单表示它用于XQuery规范中但你不能在自己的查询中使用它 < |