суммировать значения xml на основе нескольких групп - PullRequest
0 голосов
/ 08 января 2019

извините, я довольно новичок в обработке 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

мой настоящий файл содержит около 7 миллионов строк, поэтому я заинтересован в том, нужно ли использовать какой-либо потоковый анализ и будет ли XPath или XQuery работать лучше.

Я пробовал несколько XQuery, но мне трудно группировать по циклам более высокого порядка:

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

Любое понимание приветствуется!

Ответы [ 4 ]

0 голосов
/ 08 января 2019

Не ясно, нужно ли вам группировать (потому что внутри division есть дублирующие элементы division и / или дубликаты subdivision), если у вас нет дубликатов, которые вы можете использовать (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

0 голосов
/ 08 января 2019

думаю, я понял это (также удаляет пробелы):

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;')        
0 голосов
/ 08 января 2019
<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
0 голосов
/ 08 января 2019

Вы можете использовать этот простой 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, но этот запрос относительно прост, поэтому я полагаю, что он быстрый.

...