跳转到主要内容

18 · 分组行与小计输出

场景

你的发票 / 结算单 / 采购订单工作簿里有明细行,按客户或月份切成多个段落,每段后面跟一行小计,(可选)最底部还有一行总计:

北京物流 部件 A 10,000
北京物流 部件 B 5,000
小计 15,000
上海贝塔工程 部件 A 20,000
小计 20,000
总计 35,000

XTL 0.6 提供了两个指令,可以在同一个数据块内完成这件事,不需要在源里预聚合,也不需要在输出后再后处理(ADR-0038)。

两个组件

@group [Key1], [Key2], …

@group 把活动行集合分成 N 层嵌套分组,用于交错输出小计。它重新排序——分组顺序是 @filter@sort 应用之后的出现顺序。要稳定分组顺序,请配合用相同键的 @sort

{{ @sort [客户] }}
{{ @group [客户] }}

@subtotal <aggregate>

包含 {{ @subtotal SUM([金额]) }} 单元格的行就是一条小计行。它不会按每条源行迭代;而是在每个分组边界、按该行绑定的层级,由渲染器在合适的时机输出一次。支持的聚合:SUMCOUNTAVERAGEMINMAX

源顺序中的第一@subtotal 行绑定到最内层分组键。在它下方堆叠更多 @subtotal 行可绑定到外层——最底下的那行绑定到最外层键。

单层分组

{{ @sort [客户] }}
{{ @group [客户] }}
{{ [客户] }} | {{ [品项] }} | {{ [金额] }}
"小计" | | {{ @subtotal SUM([金额]) }}

对 3 条源(北京物流/部件/100、上海贝塔工程/螺栓/50、北京物流/齿轮/200)的渲染结果:

北京物流 部件 100
北京物流 齿轮 200
小计 300
上海贝塔工程 螺栓 50
小计 50

二层嵌套 + 总计

{{ @sort [区域] }}
{{ @sort [客户] }}
{{ @group [区域], [客户] }}
{{ [区域] }} | {{ [客户] }} | {{ [金额] }}
"客户小计" | | {{ @subtotal SUM([金额]) }}
"区域小计" | | {{ @subtotal SUM([金额]) }}

最靠上的 @subtotal 行(客户小计)绑定到最内层键 [客户];下一行(区域小计)绑定到 [区域]。两者都在各自的边界输出;当两个边界同时结束时,内层先 fire。

"用最外层 @subtotal 实现总计"模式:在单个 @group [客户] 配上两条 @subtotal 行时,外层那条恰好在数据块末尾 fire 一次——因为最外层分组的边界就是数据的末尾。

与其他指令的组合

指令交互
@filter过滤在分组之前应用。被过滤掉的行不属于任何分组。所有行都被过滤掉的分组直接不出现。
@sort排序在分组之前应用。要固定分组顺序,请按与 @group 相同的键、相同的顺序 @sort
@source每个 @source 块都有自己的分组作用域。
@join连接行的列像主表行的列一样参与分组。分组键可以引用被连接源的列。
@top在分组之后按行级应用。只有经过 @top 裁剪后仍有数据行的分组,其小计才会被输出。
@repeat right@group 不兼容(xl3/directive/invalid-syntax)。

边界情况

  • 单一分组退化情形 ——@group [Key] 且所有行都共享同一个 [Key] 值时,小计仍会在该分组边界输出一次。当数据集刚好只含一个外层分组值时,这与总计模式一致。
  • 空分组 ——所有数据行都为空(按 ADR-0007)的分组会被跳过:既不输出数据行,也不输出 @subtotal
  • 聚合参数 ——@subtotal 内只接受列引用。复合表达式(SUM([A]) - SUM([B])IF(...))会抛出 xl3/subtotal/bad-aggregate,并被延后。
  • @subtotal 行上的字面文本单元格 ——可以;"小计:"这种标签可以跟聚合单元格并排,并在每次输出时一起渲染。字面单元格不可引用当前行列;分组边界处没有"当前行"。

错误码

  • xl3/group/missing-key ——@group 指令缺少键列表。
  • xl3/subtotal/outside-group ——不带 @group 的块里出现了 @subtotal 单元格,或 @subtotal 行数多于 @group 键数。
  • xl3/subtotal/bad-aggregate ——主体不是 SUMCOUNTAVERAGEMINMAX 之一,或其参数不是列引用。

另见