像许多人一样,我正在处理 XML 上的 Mondial 数据库。如果 XQuery 语法没有尽最大努力进行破坏,那将是小菜一碟。
let $inland := //province/@id
where every $sea in //sea satisfies
$sea/located/@province != $inland
return $inland
Run Code Online (Sandbox Code Playgroud)
我在上面要做的是找到所有“内陆”省份,即旁边没有海的省份。然而,这是行不通的,因为 $sea/located/province 是一个大字符串,每个省份都与它接壤。
所以我试着修改成。
let $inland := //province/@id
where every $sea in //sea satisfies
not(contains($sea/located/@province, $inland))
return $inland
Run Code Online (Sandbox Code Playgroud)
我希望它只找到属于海洋接壤省份一部分的省份。简单明了。
错误信息:
Stopped at C:/Users/saffekaffe/Desktop/mondial/xml/country_without_island.xml, 2/1:
[XPTY0004] Item expected, sequence found: (attribute id {"prov-Greece-2"},....
Run Code Online (Sandbox Code Playgroud)
我该如何解决这个问题?
//sea/located/province@示例
province="prov-France-5 prov-France-20 prov-France-89 prov-France-99"
Run Code Online (Sandbox Code Playgroud)
//province/@id 示例
id="prov-Greece-2"
Run Code Online (Sandbox Code Playgroud)
XQuery 有多种以与您预期不同的方式工作的方式。
如果它们的参数中的至少一个是序列而不是单个项目,则比较运算符=和!=具有存在语义。这意味着相当于。查询返回,因为至少有一个公共项。$seq1 = $seq2some $x in $seq1, $y in $seq2 satisfies $x = $y('foo', 'bar') = ('bar', 'baz', 'quuz')true
类似 XQuery 异常的//province/@id计算结果为所有匹配节点的序列。在您的情况下,这将是超过 1000 个省份 ID 的序列:(id="prov-cid-cia-Greece-2", id="prov-cid-cia-Greece-3", id="prov-cid-cia-Greece-4", [...]). 这个序列然后绑定到$inland您的let子句中的变量。由于您不迭代$inland(例如使用for子句)中的单个项目,因此该where条件将同时作用于全球所有省份的整个序列。所以你的条件every $sea in //sea satisfies
$sea/located/@province != $inland现在意味着:
“对于每sea一个province位于它旁边的,它的一个@id不等于所有现有省份 ID 中的至少一个。”
这是返回,false因为有sea没有located孩子的s ,例如亚丁湾。
contains($str, $sub)不适合检查子字符串是否包含在以空格分隔的字符串中,因为它也匹配条目的一部分:contains("foobar baz quux", "oob")返回true。
相反,您应该使用tokenize($str)并查看其部分将字符串拆分为多个部分,或者使用contains-token($str, $token).
综上所述,与您的原始查询非常相似的正确查询是:
for $inland in //province/@id
where
every $sea in //sea
satisfies not(contains-token($sea/located/@province, $inland))
return $inland
Run Code Online (Sandbox Code Playgroud)
另一种方法是首先收集seas旁边的所有(唯一)省份,然后返回所有不在该序列中的省份:
let $next-to-sea := distinct-values(//sea/located/@province/tokenize(.))
return //province/@id[not(. = $next-to-sea)]
Run Code Online (Sandbox Code Playgroud)
更紧凑(但可能效率更低):
//province/@id[not(. = //sea/located/@province/tokenize(.))]
Run Code Online (Sandbox Code Playgroud)
在频谱的另一端,您可以使用 XQuery 3.0地图通过一次查找替换对所有海滨省份的潜在线性搜索:
let $seaside :=
map:merge(
for $id in //sea/located/@province/tokenize(.)
return map{ $id: () }
)
return //province/@id[not(map:contains($seaside, .))]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
680 次 |
| 最近记录: |