怎样用 esProc 实现连续区间的差集运算
某数据库有两个表,原库存表 data_add 存储了多个物品的多批库存,每批库存有一个起始编号 START_NUM 和一个终止编号 END_NUM,表示区间范围。
ID | ITEM_ID | START_NUM | END_NUM |
1 | 337 | 101 | 400 |
2 | 337 | 500 | 800 |
3 | 337 | 801 | 1200 |
4 | 337 | 1500 | 1600 |
5 | 337 | 15000 | 16000 |
6 | 337 | 20000 | 30000 |
7 | 444 | 20 | 30 |
消耗表 data_cons 存储了多个物品的多批消耗,每批消耗同样是个区间范围。
ID | ITEM_ID | START_NUM | END_NUM |
1 | 337 | 240 | 300 |
2 | 337 | 301 | 400 |
3 | 337 | 850 | 1100 |
4 | 337 | 1500 | 1510 |
5 | 337 | 15000 | 16000 |
现在要计算出每个物品的现库存,即原库存的多段区间和消耗的多段区间的差集,结果用多段区间来表示。原库存的区间可能被消耗成不连续的多段区间,这种情况下要自然生成多条记录,每条记录对应一个区间,比如原库存的区间 [500:1200] 被消耗成了 2 个区间 [500:849] 和[1101:1200]。
ITEM_ID | START_NUM | END_NUM |
337 | 101 | 239 |
337 | 500 | 849 |
337 | 1101 | 1200 |
337 | 1511 | 1600 |
337 | 20000 | 30000 |
444 | 20 | 30 |
SQL无法用变量表示集合,不方便进行集合的集合之间的运算,代码非常繁琐。SPL 可以用变量代表集合,容易表达各类集合运算:Try DEMO
| A | B |
1 | $select * from data_add.txt | $select * from data_cons.txt |
2 | =A1.group(ITEM_ID;~.conj(to(START_NUM,END_NUM)):a) | =B1.group(ITEM_ID;~.conj(to(START_NUM,END_NUM)):b) |
3 | =A2.join(ITEM_ID,B2,b) | |
4 | =A3.derive([a, b].merge@d().group@i(~!=~[-1]+1):diff) | |
5 | =A4.news(diff; ITEM_ID, ~1:START_NUM, ~.m(-1):END_NUM) |
A1-B1:加载数据。
A2:用 group 函数对原库存按物品分类,但不汇总,将组内的每个区间转为一个连续序列的小集合,再合并为一个大集合。~ 表示当前组,函数 to 可按起止序号生成连续序列。
B2:对消耗表进行相同的处理。
A3=A2.join(ITEM_ID,B2,b):用 join 函数按物品编号进行左关联。
A4=A3.derive([a, b].merge@d()…..)。新增计算列,先将每个物品的原库存集合和消耗集合进行差集计算。函数 merge 对有序集合进行归并,@d 表示计算差集。注意差集后的序列不连续,比如 849、1101。
group@i(~!=~[-1]+1)。再对每个差集进行条件分组,将连续的序列分到同一小组,比如 849、1101 分别分到了第 2 和第 3 组。函数 group 用于分组,默认按等值分组,@i 表示按条件分组,~[-1] 表示上一个成员。
A5=A4.news(diff; ITEM_ID, ~1:START_NUM, ~.m(-1):END_NUM)。用 A4 的每条记录的 diff 字段里的每个序列,生成一条新记录,其中,新区间的起止序号取自每个序列的头尾。函数 news 可将集合的每个成员扩展成一条记录。~.m(-1) 表示 ~ 里的倒数第 1 个成员,~.m(1) 表示正数第 1 个成员,简写做 ~1。
esProcSPL是开源免费的,点在下载试用~