如何计算XQuery中最长的持续时间?

时间:2017-04-17 22:51:02

标签: max xquery

我在XQuery中练习有问题。逻辑:

  • 获取持续时间最长的课程。

这是xml文件的结构:

<training>
    <course id="1">
        <start>20170101</start>
        <end>20170401</end>
    </course>
</training>

我做到了:

for $x in doc("LMSGI06")//course
let $max := max($x/end - $x/start)
return
    <duration>{$max}</duration>

这是我查询的结果:

<duration>300</duration>
<duration>400</duration>
<duration>400</duration>
<duration>400</duration>
<duration>10000</duration>

我的查询列出了所有课程的持续时间,但我只需要持续时间最长的课程。请注意,XML文件中的日期具有数字格式,而不是日期格式,因此,我尝试减去十进制数等日期。

3 个答案:

答案 0 :(得分:3)

如果您正在使用Saxon,那么另一种选择是撒克逊人:最高职能:

saxon:highest(doc("LMSGI06")//course, 
              function($course) { $course/(end - start) }
             )

但是,使用十进制减法而不是日期/持续时间减法并不是合理的。考虑间隔20161231-20170101(一天:小数差= 8870)与20170201-20170205(四天:小数差= 4)。第二个持续时间较长,但小数差较小。因此,您应该将值真正转换为xs:date,并将它们减去xs:date值以获取xs:duration值。

答案 1 :(得分:2)

您目前正在评估for循环中的max()。单个数字的max()就是那个数字。

您可以获得所有持续时间的max(),然后选择具有该持续时间的课程:

let $doc := doc("LMSGI06")
let $maxDuration := max(
                     for $x in $doc//course
                     return $x/end - $x/start
                    )
return $doc//course[(end - start) eq $maxDuration]

或者您可以按照其持续时间订购课程,然后选择第一个:

(
  for $course in doc("LMSGI06")//course
  order by $course/end - $course/start descending
  return $course
)[1]

答案 2 :(得分:2)

如果您不使用 Saxon 但仍希望仅处理每个course一次,则可以重新实现纯 XQuery 3.0中的saxon:highest(...)函数

declare function local:highest($seq, $key-func) {
  head(
    fold-left($seq, (),
      function($max, $curr) {
        let $key := $key-func($curr)
        return if($max[2] >= $key) then $max else ($curr, $key)
      }
    )
  )
};

可以将日期字符串转换为xs:date数据类型,如下所示:

declare function local:to-date($str) {
  xs:date(concat(substring($str, 1, 4), '-',
    substring($str, 5, 2), '-', substring($str, 7)))
};

使用这两个功能,正确的日期比较练习的解决方案变得非常简单:

local:highest(
  doc("LMSGI06")//course, 
  function($course) {
    local:to-date($course/end) - local:to-date($course/start)
  }
)