【问题标题】:How to avoid duplicates in XQuery results?如何避免 XQuery 结果中的重复?
【发布时间】:2016-02-15 19:37:40
【问题描述】:

这是新手问题。我最近才开始学习 XQuery 和 XPath。

考虑这个 XML

<employees>
  <employee empid="1">
    <ename>KING</ename>
    <mgr/>
    <hiredate>17-11-1981</hiredate>
  </employee>
  <employee empid="2">
    <ename>BLAKE</ename>
    <mgr>7839</mgr>
    <hiredate>1-5-1981</hiredate>
    <test>
      <sub1>one</sub1>
      <sub2>two</sub2>
    </test>
  </employee>
</employees>

当我执行以下 XQuery 时,

let $db := db:open("example")
for $item1 in $db/employees/employee,
    $item2 in $db/employees/employee/test
return ($item1/mgr,$item1/ename,$item2/sub1)

我得到...

<mgr/>
<ename>KING</ename>
<sub1>one</sub1>
<mgr>7839</mgr>
<ename>BLAKE</ename>
<sub1>one</sub1>

我希望得到的结果是……

<mgr/>
<ename>KING</ename>
<mgr>7839</mgr>
<ename>BLAKE</ename>
<sub1>one</sub1>

这是因为 sub1 只存在于 /employee/@empid='2' 中。

有人可以指点我正确的方向吗?谢谢

【问题讨论】:

  • 您的示例 XML 数据已损坏 - &lt;employees&gt; 元素缺少结束标记。

标签: xml xpath xquery basex


【解决方案1】:

在你的for 表达式中

for $item1 in $db/employees/employee,
    $item2 in $db/employees/employee/test

$item1 遍历所有employees/employee 元素,$item2 遍历所有employees/employee/test 元素,独立于$item1 的当前值。

要得到你需要的东西,你可以试试这个:

for $item1 in $db/employees/employee,
return ($item1/mgr,$item1/ename,
    for $item2 in $item1/test
    return $item2/sub1)

或者,更短:

$db/employees/employee/(mgr, ename, test/sub1)

【讨论】:

  • 谢谢,我尝试了您的建议,它确实有效。但是,即使没有 XQuery 语法中的子元素,我也无法让简短版本工作。以下内容对您来说是否正确? let $db := db:open("example")for $item1 in $db/employees/employee/(mgr, ename)return ($item1/mgr,$item1/ename)
  • 没关系,我明白了。正确的语法是let $db := db:open("example") for $item1 in $db/employees/employee/(mgr, ename, test/sub1) return $item1 谢谢!
  • @beyonddc $db/employees/employee/(mgr, ename, test/sub1) 也是一个有效的 XQuery 表达式,正如 CiaPan 向您展示的那样。它与for $item1 in $db/employees/employee/(mgr, ename, test/sub1) return $item1 完全相同。
  • 我的表达式的问题可能是 XPath 版本?当我选择 XPath 2.0 时,XPath tester site 会正确执行此操作,但对于 1.0,它会显示“在 '/' 或 '//' 标记之后需要一个定位步骤。”好像它不理解路径表达式中的序列构造......
【解决方案2】:

这是一种方法。在返回之前,使用if 语句检查test/sub1 是否存在。

我也稍微更改了 for 子句,删除了 db:open() 函数,但很容易重新添加它。

for $employee in /employees/employee
return
if ($employee/test/sub1)
then
($employee/mgr, $employee/ename, $employee/test/sub1)
else
($employee/mgr, $employee/ename)

结果是

<mgr/>
<ename>KING</ename>
<mgr>7839</mgr>
<ename>BLAKE</ename>
<sub1>one</sub1>

您说有不止一种方法可以做到这一点,请您进一步描述一下

稍微不同的方法是使用if/then/else XPath 表达式并无条件返回此表达式:

for $employee in /employees/employee
return
($employee/mgr, $employee/ename, if ($employee/test/sub1) then $employee/test/sub1 else '')

CiaPan 的回答显示了另一种方式,这次是嵌套 for 子句。在我看来,它有点不那么简单,但它仍然有效。 CiaPan 还展示了单个 XPath 表达式如何潜在地解决问题 - 这非常简单!

【讨论】:

  • 谢谢,这绝对适用于我上面描述的用例。你说有不止一种方法可以做到这一点,你能否进一步描述或指出我可以做更多研究的某些术语?在我的应用程序中,xquery 语句将根据开始从 XML 模式的树视图拖到表中的元素生成。我可以看到,当开始将多个子元素拖到我的表中时,这些 if 语句将开始累加。应用程序的最终结果是以表格格式显示 XML 数据。
  • @beyonddc 请查看我的回答的编辑。我看不到被拖到表格的项目和从输入 XML 中选择元素之间的连接。您需要编辑您的问题并包含理解该问题所需的所有信息。
  • xquery 语句实际上是由我的应用程序自动生成的。我遇到的问题是 xquery 语句的结构,而不是应用程序本身,因此我没有在此处包含有关我的应用程序的详细信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-20
  • 1970-01-01
  • 2012-06-29
  • 2018-05-03
  • 2019-08-20
  • 2016-12-02
  • 1970-01-01
相关资源
最近更新 更多