xquery:如何获得不同的节点值

时间:2012-10-02 15:15:22

标签: xml xquery distinct-values

我想知道Xquery是否有任何类似于distinct-values但返回节点的函数。

让我更清楚一点:例如我有一个参考书目,对于每个作者,我想列出他写的所有书。我的具体案例中的作者元素是这样的:

<author>
  <last> Shakespear </last>
  <first> William </first>
</author>

在作者上使用distinct-values返回ShakespearWilliam,据我所知,它没有帮助。我想要一个保留元素作者结构而不考虑重复的函数。

如果您找到另一种查询方式,请告诉我。有没有人有任何想法?

2 个答案:

答案 0 :(得分:4)

获取不同节点的问题是如何确定两个节点是不同的。这是XML中的一个复杂主题。如果重复节点具有相同的节点标识(即:它们引用同一节点),则可以使用类似functx:distinct-nodes()的函数。否则,您需要某种类型的哈希来确定节点是否“足够相等”以使其相等,或者使用deep-equal()进行比较,这对大型数据集执行效果不佳。

如果两个<author>在最后一个名字相同时相等,那么你可以使用像concat(last,first)那样简单的东西作为哈希值,并使用xpath获取不同的值:

$xml/author[index-of($xml/author/concat(last,first), concat(last,first))[1]]

这仍然不理想,因为您在每一步都计算哈希值,因此对于大型数据集来说它会变慢。为了提高性能,您可以做的一件事是预先计算数据的哈希值,即:

<author hash="ShakespearWilliam">
  <last>Shakespear</last>
  <first>William</first>
</author>

$xml/author[index-of($xml/author/@hash, @hash)[1]]

如果您可以通过散列(理想情况下使用有序数据库索引)有效地获取有序节点,那么有一种更有效的删除重复项的方法:

declare function local:nodupes($first, $rest)
{
    if (empty($rest)) then $first
    else if ($first/@hash eq $rest[1]/@hash)
    then local:nodupes($rest[1], subsequence($rest,2))
    else ($first, local:nodupes($rest[1], subsequence($rest,2)))
};

然后用您订购的套装调用它:

let $ordered :=
  for $a in $xml/author
  order by $a/@hash
  return $a
return 
  local:nodupes((),$ordered)

答案 1 :(得分:2)

XQuery 3.0有一个“分组依据”构造,这允许您通过(名字,姓氏)的值对作者进行分组。当您对节点进行分组时,您基本上可以得到答案:当且仅当节点位于不同的组中时,节点才是不同的。

有很多产品可以实现XQuery 3.0草案的这一部分; Saxon 9.4就是其中之一。