PAYATHON 2026

如何用 JCL 和 DFSORT 将记录均衡拆分到 N 个文件并保留学生分组?

数据阿明

可以实现,先确定分组边界

可以通过 JCL 编排处理步骤,再由 DFSORT 输出文件。记录数和 N 都可能变化时,可以先统计每个 Student number 的记录数,计算允许的拆分位置,再生成 DFSORT 控制语句。

不能直接依靠 SPLIT、SPLITBY 或 SPLIT1R 保证学生分组完整。这些参数按记录分配输出,不会自动把同一个 Student number 的全部记录视为不可拆分的单位。相关行为见 IBM OUTFIL 文档。

JCL 负责调用程序和连接数据集,边界的选择需要在处理逻辑中明确。

先把“尽量均衡”定义为具体规则

示例共有 26 条数据记录,各学生的记录数为:

Student number记录数累计记录数
AA00000144
AA000002812
AA000003113
AA000004114
AA000005115
AA000006116
AA000007925
AA000008126

每个文件的平均目标是 26 / 5 = 5.2 条。AA000007 有 9 条记录,只要不拆开这个学生,就一定有一个文件至少包含 9 条记录。

如果希望像示例一样,让各输出文件依次保存原文件中的连续区间,可以对每个理想累计位置选择最近的学生组结束位置。距离相同时,固定选择较早的位置。

这个规则能复现题目中的结果,但不保证在所有数据分布下都让各文件的记录数差异最小。

根据累计记录数选择切分点

设总记录数为 T,输出文件数为 N,第 j 个理想切分位置为:

q_j = j × T / N,其中 j = 1, 2, ..., N - 1。

允许切分的位置只能是学生组的末尾;另外加入位置 0,表示文件开头。示例中的候选位置为:

0, 4, 12, 13, 14, 15, 16, 25, 26

选择结果为:

理想累计位置最近的分组边界
5.24
10.412
15.616
20.825

五个输出文件对应的记录区间为:

OUT1:  1 - 4
OUT2:  5 - 12
OUT3: 13 - 16
OUT4: 17 - 25
OUT5: 26 - 26

记录数分别为 4、8、4、9、1,与期望输出一致。这里的记录号指数据记录在输入流中的位置,不依赖示例末列的 Rec Seq Number 字段。

使用 DFSORT 输出这些区间

输入已经排序,并且只包含示例中的 26 条数据记录时,可以使用以下 DFSORT 控制语句:

  OPTION COPY
  OUTFIL FNAMES=OUT1,STARTREC=1,ENDREC=4
  OUTFIL FNAMES=OUT2,STARTREC=5,ENDREC=12
  OUTFIL FNAMES=OUT3,STARTREC=13,ENDREC=16
  OUTFIL FNAMES=OUT4,STARTREC=17,ENDREC=25
  OUTFIL FNAMES=OUT5,STARTREC=26,ENDREC=26

STARTREC 和 ENDREC 指定该 OUTFIL 接收的相对记录号范围,包含两个端点。相对记录号从 1 开始。具体定义见 IBM OUTFIL 控制语句说明。

在 JCL 中,需要将 SORTIN 指向输入数据集,将 OUT1 到 OUT5 分别定义为输出 DD,并通过 SYSIN 提供上述控制语句。输出数据集的分配参数应根据实际记录格式和站点要求填写。

这组控制语句只适用于当前样例。输入数量或学生分布变化后,必须重新计算边界。

推广到一般的 N

可将作业分成三个处理步骤:

  1. 顺序扫描已排序的输入,在 Student number 发生变化时结束上一组,记录组大小和累计记录数。
  2. 使用辅助程序计算边界,生成本次运行所需的 DFSORT 控制语句。辅助程序可以采用站点支持的 COBOL 或 REXX 等语言实现。
  3. 再次读取同一份输入,由 DFSORT 按生成的区间输出。

边界计算逻辑可以写成以下伪代码:

require N >= 1

boundaries = [0] + cumulative_group_counts
T = boundaries[-1]
cuts = [0]

for j = 1 to N - 1:
    choose b from boundaries minimizing abs(N * b - j * T)
    if tied, choose the smaller b
    append b to cuts

append T to cuts

for k = 1 to N:
    start = cuts[k - 1] + 1
    end = cuts[k]

    if start <= end:
        generate an OUTFIL range for OUTk
    else:
        mark OUTk as an empty output

使用 abs(N * b - j * T) 可以避免浮点除法。实现时要确保数值类型能够容纳乘法结果。

不同理想位置可能选中同一个边界,从而出现空文件,这符合题目的许可条件。空区间应单独处理,不能生成 STARTREC > ENDREC 的控制语句;作业仍需按约定创建对应的空输出数据集。

实施时需要确认的细节

  • 统计和最终拆分应读取同一份数据,否则按记录号计算的边界可能落入某个学生组内部。
  • 上述 26 条记录不包含示例中的说明行和列标题。如果实际文件存在表头或尾记录,需要明确其处理方式。
  • Student number 必须按完整字段识别。示例是 8 个字符,不能只比较后面的 6 位数字。实际字段位置、RECFM 和 LRECL 尚未提供,统计程序需要根据这些信息读取键值。
  • 范围依据 OUTFIL 接收到的记录流。如果另外加入过滤、去重或改变记录数量的处理,边界统计必须采用相同的数据口径。
  • 该方案均衡的是记录数量,记录数只是负载的近似。若不同记录的处理成本差异很大,还需要按业务成本定义权重。

运行后,应核对输出记录总数是否仍为 26,并检查每个 Student number 是否只出现在一个输出文件中。对于动态输入,应使用本次统计得到的总数进行核对。

备注:内容仅供参考。