SPL:获取分组子集后再统计
把集合中具有相同属性的成员分配到同一个组,这就是分组运算。比如员工表根据部门分组,每组的员工都具有相同的部门;销售表可以根据销售年份分组,每组都是同一个年份的销售记录等等。
分组运算的实质是将一个集合按照某种规则拆分成若干个子集,也就是说,返回值应当是一个由集合构成的集合。对于每个成员集合,我们称为分组子集。
通常人们对分组子集的聚合值更感兴趣,因此分组运算常常伴随着对子集的进一步汇总计算。但是,我们仍然有对这些分组子集而不是聚合值更感兴趣的时候。
我们以员工表为例,部分数据如下:
ID |
NAME |
BIRTHDAY |
STATE |
DEPT |
SALARY |
1 |
Rebecca |
1974/11/20 |
California |
R&D |
7000 |
2 |
Ashley |
1980/07/19 |
New York |
Finance |
11000 |
3 |
Rachel |
1970/12/17 |
New Mexico |
Sales |
9000 |
4 |
Emily |
1985/03/07 |
Texas |
HR |
7000 |
5 |
Ashley |
1975/05/13 |
Texas |
R&D |
16000 |
… |
… |
… |
… |
… |
… |
【例 1】找出有相同出生日期的员工。
在 SPL 中提供了函数 A.group(),我们可以用它来实现真正的分组,分组后的结果集是由多个分组子集组成的集合。
SPL 脚本如下:
A |
|
1 |
=T("Employee.csv") |
2 |
=A1.group(BIRTHDAY) |
3 |
=A2.select(~.len()>1).conj() |
A1:导入员工表。
A2:使用了函数 A.group() 按出生日期分组。
A3:选择成员数量大于 1 的分组,即有相同出生日期的子集。再将这些子集合并。
严格来说,分组和汇总是两个独立的动作,但在 SQL 中总是一起出现,从而给人一种两者必须同时使用的假象。事实上,这种组合是对分组操作的一种局限,或者说分组之后,能够进行的计算远不止 SQL 中的几种聚合函数。
【例 2】查询年龄低于部门平均年龄的员工。
前面已经介绍过,在 SPL 中函数 A.group()用于分组。我们可以在函数 A.group() 中,定义在分组后对每个分组子集的运算。不限于 SUM、COUNT 等聚合运算,可以定义一些复杂运算。
SPL脚本如下:
A |
|
1 |
=T("Employee.csv") |
2 |
=A1.group(DEPT; (a=~.avg(age(BIRTHDAY)), ~.select(age(BIRTHDAY)<a)):YOUNG) |
3 |
=A2.conj(YOUNG) |
A1:导入员工表。
A2:按部门分组,并在每个分组中选出年龄低于平均年龄的记录。在函数 A.group() 的聚合运算中,我们可以使用临时变量,使得运算更加简单易懂。
A3:将选出的记录合并。
【例 3】选出员工人数超过 50 人的州,查询这些州各部门的平均工资。
分组本身是复杂运算,保留分组子集可重复使用,提高运行效率。在 SPL 中,函数 A.group() 在分组后可以保留分组子集,可以针对分组子集再次进行分组汇总等运算。
SPL脚本如下:
A |
|
1 |
=T("Employee.csv") |
2 |
=A1.group(STATE).select(~.len()>50) |
3 |
=A2.conj(~.groups(DEPT; avg(SALARY):AVG_SALARY).derive(A2.~.STATE:STATE)) |
A1:导入员工表。
A2:按州进行分组,并选出员工数大于 50 人的分组子集。
A3:对每个州的分组子集再按部门分组汇总平均工资,再把这些结果集合并。