我理解慕尼黑方法在第一次阅读时索引了整个文档。但如何才能在分组之前或分组内排序而不是按其键排序呢?具体来说,如何对键的同级进行排序以计算一系列元素值的最小值和最大值?
如下面的 XML 数据所示,我正在尝试按行业汇总公司。在七个指标(收入、资产、股权、净利润、股价和员工)中,我可以实现各种汇总:用
sum()
求和,用 sum() div count()
求平均值。但为了获得净利润的最小值和最大值,我需要按净利润对行业内的公司进行排序,并选择 position()
处的值。请注意,每个行业恰好有五家公司。因此,对于净收入递减排序,position()=1
是最大值,position()=5
是最小值。
现在,我可以运行 two XSLT 脚本来实现我想要的结果。第一个排序,第二个聚合。但是我如何使用 one XSLT 来做到这一点?在 Muenchian 键的
for each loop
内,我尝试了以下所有方法,但均无济于事。
<xsl:sort select="../netincome" data-type="number" order="descending"/>
<xsl:sort select="key('indkey', .)/../netincome" data-type="number" order="descending"/>
<xsl:sort select="key('indkey', .)[../netincome]" data-type="number" order="descending"/>
可能,最小值和最大值必须在其他模板之外的模板中完成,或者运行 XSLT 脚本的两个传递/调用模板或使用
<xsl:with-params>
。
XML 数据
<?xml version="1.0" encoding="UTF-8"?>
<data>
<bigcompany>
<company>Company OA</company>
<industry>Oil & Gas</industry>
<revenue>394105000000</revenue>
<assets>349493000000</assets>
<equity>174399000000</equity>
<netincome>32520000000</netincome>
<stockprice>89.38</stockprice>
<employees>75300</employees>
</bigcompany>
<bigcompany>
<company>Company OB</company>
<industry>Oil & Gas</industry>
<revenue>200494000000</revenue>
<assets>266026000000</assets>
<equity>156191000000</equity>
<netincome>19241000000</netincome>
<stockprice>108.62</stockprice>
<employees>64700</employees>
</bigcompany>
<bigcompany>
<company>Company OC</company>
<industry>Oil & Gas</industry>
<revenue>13807000000</revenue>
<assets>4726000000</assets>
<equity>16445000000</equity>
<netincome>2720000000</netincome>
<stockprice>48.5</stockprice>
<employees>22000</employees>
</bigcompany>
<bigcompany>
<company>Company OD</company>
<industry>Oil & Gas</industry>
<revenue>97800000000</revenue>
<assets>30500000000</assets>
<equity>10800000000</equity>
<netincome>2700000000</netincome>
<stockprice>27.53</stockprice>
<employees>45340</employees>
</bigcompany>
<bigcompany>
<company>Company OE</company>
<industry>Oil & Gas</industry>
<revenue>62004000000</revenue>
<assets>117144000000</assets>
<equity>48427000000</equity>
<netincome>8428000000</netincome>
<stockprice>66.66</stockprice>
<employees>16900</employees>
</bigcompany>
<bigcompany>
<company>Company PA</company>
<industry>Pharmaceuticals</industry>
<revenue>49605000000</revenue>
<assets>169274000000</assets>
<equity>71622000000</equity>
<netincome>9135000000</netincome>
<stockprice>30.14</stockprice>
<employees>78000</employees>
</bigcompany>
<bigcompany>
<company>Company PB</company>
<industry>Pharmaceuticals</industry>
<revenue>48047000000</revenue>
<assets>105128000000</assets>
<equity>56943000000</equity>
<netincome>6272000000</netincome>
<stockprice>55.43</stockprice>
<employees>76000</employees>
</bigcompany>
<bigcompany>
<company>Company PC</company>
<industry>Pharmaceuticals</industry>
<revenue>74331000000</revenue>
<assets>131119000000</assets>
<equity>69752000000</equity>
<netincome>16323000000</netincome>
<stockprice>102.31</stockprice>
<employees>126500</employees>
</bigcompany>
<bigcompany>
<company>Company PD</company>
<industry>Pharmaceuticals</industry>
<revenue>23113000000</revenue>
<assets>35249000000</assets>
<equity>17641000000</equity>
<netincome>4685000000</netincome>
<stockprice>67.2</stockprice>
<employees>37925</employees>
</bigcompany>
<bigcompany>
<company>Company PE</company>
<industry>Pharmaceuticals</industry>
<revenue>15879000000</revenue>
<assets>33749000000</assets>
<equity>14852000000</equity>
<netincome>2004000000</netincome>
<stockprice>58</stockprice>
<employees>28000</employees>
</bigcompany>
<bigcompany>
<company>Company MA</company>
<industry>Media</industry>
<revenue>48813000000</revenue>
<assets>84186000000</assets>
<equity>44958000000</equity>
<netincome>8004000000</netincome>
<stockprice>93.65</stockprice>
<employees>180000</employees>
</bigcompany>
<bigcompany>
<company>Company MB</company>
<industry>Media</industry>
<revenue>64657000000</revenue>
<assets>158813000000</assets>
<equity>51058000000</equity>
<netincome>7135000000</netincome>
<stockprice>57.05</stockprice>
<employees>139000</employees>
</bigcompany>
<bigcompany>
<company>Company MC</company>
<industry>Media</industry>
<revenue>31867000000</revenue>
<assets>54793000000</assets>
<equity>17418000000</equity>
<netincome>4514000000</netincome>
<stockprice>36.52</stockprice>
<employees>27000</employees>
</bigcompany>
<bigcompany>
<company>TCompany MD</company>
<industry>Media</industry>
<revenue>29795000000</revenue>
<assets>67994000000</assets>
<equity>29904000000</equity>
<netincome>3691000000</netincome>
<stockprice>84.3</stockprice>
<employees>26000</employees>
</bigcompany>
<bigcompany>
<company>Company ME</company>
<industry>Media</industry>
<revenue>15284000000</revenue>
<assets>26387000000</assets>
<equity>9966000000</equity>
<netincome>1879000000</netincome>
<stockprice>54.88</stockprice>
<employees>20915</employees>
</bigcompany>
</data>
XSLT 1
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="data">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort select="industry" order="ascending"/>
<xsl:sort select="netincome" data-type="number" order="descending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
XSLT 2
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="indkey" match="bigcompany/industry" use="."/>
<xsl:template match="data">
<data>
<xsl:for-each select="bigcompany/industry[generate-id()
= generate-id(key('indkey', .)[1])]">
<xsl:sort select="." order="ascending"/>
<aggdata>
<xsl:copy-of select="."/>
<SumOfRevenue><xsl:copy-of select="sum(key('indkey', .)/../revenue)"/></SumOfRevenue>
<AvgOfAssets><xsl:copy-of select="sum(key('indkey', .)/../assets) div count(key('indkey', .)/../assets)"/></AvgOfAssets>
<AvgOfEquity><xsl:copy-of select="sum(key('indkey', .)/../equity) div count(key('indkey', .)/../equity)"/></AvgOfEquity>
<MaxOfIncome><xsl:value-of select="key('indkey', .)[1]/../netincome"/></MaxOfIncome>
<MinOfIncome><xsl:value-of select="key('indkey', .)[5]/../netincome"/></MinOfIncome>
<AvgOfStockPrice><xsl:copy-of select="sum(key('indkey', .)/../stockprice) div count(key('indkey', .)/../stockprice)"/></AvgOfStockPrice>
<SumOfEmployees><xsl:copy-of select="sum(key('indkey', .)/../employees)"/></SumOfEmployees>
</aggdata>
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>
最终和期望的结果 - 但是如何使用一个 XSLT?
<?xml version='1.0' encoding='UTF-8'?>
<data>
<aggdata>
<industry>Media</industry>
<SumOfRevenue>1.90416e+011</SumOfRevenue>
<AvgOfAssets>7.84346e+010</AvgOfAssets>
<AvgOfEquity>3.06608e+010</AvgOfEquity>
<MaxOfIncome>8004000000</MaxOfIncome>
<MinOfIncome>1879000000</MinOfIncome>
<AvgOfStockPrice>65.28</AvgOfStockPrice>
<SumOfEmployees>392915</SumOfEmployees>
</aggdata>
<aggdata>
<industry>Oil & Gas</industry>
<SumOfRevenue>7.6821e+011</SumOfRevenue>
<AvgOfAssets>1.535778e+011</AvgOfAssets>
<AvgOfEquity>8.12524e+010</AvgOfEquity>
<MaxOfIncome>32520000000</MaxOfIncome>
<MinOfIncome>2700000000</MinOfIncome>
<AvgOfStockPrice>68.138</AvgOfStockPrice>
<SumOfEmployees>224240</SumOfEmployees>
</aggdata>
<aggdata>
<industry>Pharmaceuticals</industry>
<SumOfRevenue>2.10975e+011</SumOfRevenue>
<AvgOfAssets>9.49038e+010</AvgOfAssets>
<AvgOfEquity>4.6162e+010</AvgOfEquity>
<MaxOfIncome>16323000000</MaxOfIncome>
<MinOfIncome>2004000000</MinOfIncome>
<AvgOfStockPrice>62.616</AvgOfStockPrice>
<SumOfEmployees>346425</SumOfEmployees>
</aggdata>
</data>
这样怎么样?
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="co-by-ind" match="bigcompany" use="industry" />
<xsl:template match="/data">
<xsl:copy>
<xsl:for-each select="bigcompany[generate-id() = generate-id(key('co-by-ind', industry)[1])]">
<xsl:sort select="industry" data-type="text" order="ascending"/>
<!-- variables -->
<xsl:variable name="curr-group" select="key('co-by-ind', industry)" />
<xsl:variable name="income-sorted">
<xsl:for-each select="$curr-group">
<xsl:sort select="netincome" data-type="number" order="ascending"/>
<xsl:copy-of select="netincome"/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="income-sorted-set" select="exsl:node-set($income-sorted)/netincome" />
<!-- output -->
<aggdata>
<xsl:copy-of select="industry"/>
<SumOfRevenue>
<xsl:value-of select="sum($curr-group/revenue)"/>
</SumOfRevenue>
<AvgOfAssets>
<xsl:value-of select="sum($curr-group/assets) div count($curr-group/assets)"/>
</AvgOfAssets>
<AvgOfEquity>
<xsl:value-of select="sum($curr-group/equity) div count($curr-group/equity)"/>
</AvgOfEquity>
<MaxOfIncome>
<xsl:value-of select="$income-sorted-set[last()]"/>
</MaxOfIncome>
<MinOfIncome>
<xsl:value-of select="$income-sorted-set[1]"/>
</MinOfIncome>
<AvgOfStockPrice>
<xsl:value-of select="sum($curr-group/stockprice) div count($curr-group/stockprice)"/>
</AvgOfStockPrice>
<SumOfEmployees>
<xsl:value-of select="sum($curr-group/employees)"/>
</SumOfEmployees>
</aggdata>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这是一个更短、更简单的 XSLT 1.0 解决方案,不使用任何扩展功能:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kByInd" match="bigcompany" use="industry"/>
<xsl:template match="/*">
<data>
<xsl:apply-templates select=
"bigcompany[generate-id() = generate-id(key('kByInd', industry)[1])]">
<xsl:sort select="industry"/>
</xsl:apply-templates>
</data>
</xsl:template>
<xsl:template match="bigcompany">
<xsl:variable name="vGroup" select="key('kByInd', industry)"/>
<xsl:variable name="vgroupCount" select="count($vGroup)"/>
<xsl:variable name="vMaxMin">
<xsl:apply-templates select="$vGroup/netincome">
<xsl:sort data-type="number"/>
</xsl:apply-templates>
</xsl:variable>
<aggdata>
<xsl:copy-of select="industry"/>
<SumOfRevenue><xsl:copy-of select="sum($vGroup/revenue)"/></SumOfRevenue>
<AvgOfAssets><xsl:copy-of select="sum($vGroup/assets) div $vgroupCount"/></AvgOfAssets>
<AvgOfEquity><xsl:copy-of select="sum($vGroup/equity) div $vgroupCount"/></AvgOfEquity>
<MaxOfIncome><xsl:copy-of select="substring-before(substring-after($vMaxMin, '|'), '|')"/></MaxOfIncome>
<MinOfIncome><xsl:copy-of select="substring-before($vMaxMin, '|')"/></MinOfIncome>
<AvgOfStockPrice><xsl:copy-of select="sum($vGroup/stockprice) div $vgroupCount"/></AvgOfStockPrice>
<SumOfEmployees><xsl:copy-of select="sum($vGroup/employees)"/></SumOfEmployees>
</aggdata>
</xsl:template>
<xsl:template match="netincome">
<xsl:if test="position() = 1 or position() = last()">
<xsl:value-of select="concat(., '|')"/>
</xsl:if>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>