基于多个分组对xml值求和

时间:2019-01-08 00:55:52

标签: xml xslt xquery

对不起,我是xml处理的新手……我有以下内容:

<divisions>
  <division>
    <divisionName>D1</divisionName>
    <subdivisions>
      <subdivision>
        <subdivisionName>SD1</subdivisionName>
        <values>
          <value>1</value>
        </values>
      </subdivision>
      <subdivision>
        <subdivisionName>SD2</subdivisionName>
        <values>
          <value>1</value>
          <value>2</value>
        </values>
      </subdivision>
    </subdivisions>
  </division>
  <division>
    <divisionName>D2</divisionName>
    <subdivisions>
      <subdivision>
        <subdivisionName>SD3</subdivisionName>
        <values>
          <value>2</value>
          <value>2</value>
        </values>  
      </subdivision>
    </subdivisions>
  </division>
</divisions>
我想使用XPath或XQuery转换为

一个平面文件,其中按分区和细分求和。因此对于上面的结果将是:

D1 SD1 1
D1 SD2 3
D2 SD3 4

我的实际文件大约有700万行,所以我对是否有必要使用某种流形式的解析以及XPath或XQuery的性能最佳感兴趣。

我尝试了许多XQuery,但发现很难按高阶循环进行分组:

for all divisions
  for all subdivisions
    print divisionName, subdivisionName, sum(values)

任何见解表示赞赏!

4 个答案:

答案 0 :(得分:1)

您可以使用此简单的XQuery。 declare语句仅用于设置正确的输出模式。

xquery version "1.0";
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization"; 
declare option output:method "text"; 
let $db := doc("test.xml")/divisions 
for $x in $db/division, $y in $x//subdivision
return concat(distinct-values($x/divisionName), ' ', distinct-values($y/subdivisionName), ' ', sum($y/values/value),'&#xa;')

其输出(经过Saxon-9测试)

D1 SD1 1
 D1 SD2 3
 D2 SD3 4

我没有将它与XSLT实现进行比较,但是此查询相对简单,因此我猜它很快。

答案 1 :(得分:1)

认为我知道了这一点(也删除了空格):

for $divisionName in distinct-values(//divisionName)
    for $subdivisionName in distinct-values(//subdivisionName)
        return concat($divisionName,$subdivisionName,sum(//division[divisionName = $divisionName]//subdivision[subdivisionName = $subdivisionName]//value),'&#xa;')        

答案 2 :(得分:0)

<xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="divisions">
        <xsl:for-each select="division">
            <xsl:for-each select="subdivisions/subdivision">
                <xsl:value-of select="ancestor::subdivisions/preceding-sibling::divisionName"/><xsl:text> </xsl:text>
                <xsl:value-of select="subdivisionName"/><xsl:text> </xsl:text>
                <xsl:value-of select="sum(values/value)"/>
                <xsl:text>&#x0a;</xsl:text>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
You may try in xslt

答案 3 :(得分:0)

尚不清楚是否需要分组(因为division中有重复的subdivision元素和/或重复的division元素),如果您没有重复项,可以使用(XQuery 3.1)

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";

declare option output:method 'text';
declare option output:item-separator  '&#10;';

for $d in divisions/division,
    $sd in $d/subdivisions/subdivision
return $d/divisionName/data() || ' ' || $sd/subdivisionName/data() || ' ' || sum($sd/values/value)

https://xqueryfiddle.liberty-development.net/bFukv8j

如果您需要分组,则将XQuery用作group-by子句,例如

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";

declare option output:method 'text';
declare option output:item-separator  '&#10;';

for $d in divisions/division
group by $dn := $d/divisionName
for $sd in $d/subdivisions/subdivision
group by $dn, $sdn := $sd/subdivisionName
return $dn || ' ' || $sdn || ' ' || sum($sd/values/value)

https://xqueryfiddle.liberty-development.net/bFukv8j/2

关于性能,您可能需要检查所选的XQuery处理器,在XQuery数据库中,它始终取决于我认为的数据库组织和索引。

XSLT 3已进行流处理,但是由于您的元素在子元素中具有分组键,因此您需要复制项目:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:mode streamable="yes"/>

  <xsl:output method="text" />

  <xsl:template match="divisions">
     <xsl:for-each-group select="division!copy-of()!subdivisions/subdivision" composite="yes" group-by="ancestor::division/divisionName, subdivisionName">
        <xsl:value-of select="current-grouping-key(), sum(current-group()/values/value)" separator=" "/>
        <xsl:text>&#10;</xsl:text>
     </xsl:for-each-group>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/gWvjQeJ