结构化数据上的选出运算
【摘要】
选出是指在集合中,根据指定条件获取成员。选出与定位计算很相似,不过定位关心的是成员在集合中的位置,而选出关心的是成员记录的详细信息。比如选出入职超过 10 年的员工收入,查询销售额超过一万的客户名称等等。如何简单快捷的实现选出?这里将为你全程剖析,并提供 esProc SPL 示例代码。 结构化数据上的选出运算
1. 选出符合条件的成员
【例 1】 分栏列出欧洲和非洲人口超 200 万的城市名称及人口(每栏按从多到少排序)。世界城市人口表部分数据如下:
Continent |
Country |
City |
Population |
Africa |
Egypt |
Cairo |
6789479 |
Asia |
China |
Shanghai |
24240000 |
Europe |
Britain |
London |
7285000 |
… |
… |
… |
… |
期望结果如下:
Europe City |
Population |
Africa City |
Population |
Moscow |
8389200 |
Cairo |
6789479 |
London |
7285000 |
Kinshasa |
5064000 |
St Petersburg |
4694000 |
Alexandria |
3328196 |
… |
… |
… |
… |
【SPL 脚本】
A |
B |
|
1 |
=connect("db").query("select * from World where Continent in('Europe','Africa') and Population >= 2000000") |
/连接数据库,取出欧洲和非洲超过 200 万人口的记录 |
2 |
=A1.select(Continent:"Europe") |
/使用 A.select() 函数取出欧洲数据 |
3 |
=A1.select(Continent:"Africa") |
/使用 A.select() 函数取出非洲数据 |
4 |
=create('Europe City',Population,'Africa City', Population) |
/按目标结构创建一个空表 |
5 |
=A4.paste(A2.(City),A2.(Population),A3.(City),A3.(Population)) |
/使用 A.paste() 函数将值粘贴到对应列 |
A4的执行结果如下:
Europe City |
Population |
Africa City |
Population |
Moscow |
8389200 |
Cairo |
6789479 |
London |
7285000 |
Kinshasa |
5064000 |
St Petersburg |
4694000 |
Alexandria |
3328196 |
… |
… |
… |
… |
【例 2】 查询纽约研发部门员工的全名和收入。部分数据如下:
ID |
NAME |
SURNAME |
STATE |
DEPT |
SALARY |
1 |
Rebecca |
Moore |
California |
R&D |
7000 |
2 |
Ashley |
Wilson |
New York |
Finance |
11000 |
3 |
Rachel |
Johnson |
New Mexico |
Sales |
9000 |
4 |
Emily |
Smith |
Texas |
HR |
7000 |
5 |
Ashley |
Smith |
Texas |
R&D |
16000 |
… |
… |
… |
… |
… |
… |
【SPL 脚本】
A |
B |
|
1 |
=connect("db") |
/连接数据源 |
2 |
=A1.query("select * from Employee") |
/导入员工表 |
3 |
=A2.select(STATE=="New York"&&DEPT=="R&D") |
/使用函数 A.select() 选出纽约研发部门员工的记录。需要同时满足所有条件时,可以使用符号 "&&"。 |
4 |
=A3.new(NAME+" "+SURNAME:FULLNAME, SALARY) |
/使用符号 + 连接字符串,组成全名 |
A4的执行结果如下:
FULLNAME |
SALARY |
Matthew Johnson |
6000 |
Lauren Thomas |
12000 |
Brooke Williams |
12000 |
【例 3】 查询各部门 30 岁以下和 50 岁以上的员工的人数。部分数据如下:
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 |
… |
… |
… |
… |
… |
… |
【SPL 脚本】
A |
B |
|
1 |
=connect("db") |
/连接数据源 |
2 |
=A1.query("select * from Employee") |
/导入员工表 |
3 |
=A2.select((age=age(BIRTHDAY), age<30 || age>50)) |
/使用函数 A.select() 选出年龄小于 30 或者年龄大于 50 的员工。需要满足多个条件中任意一个时,可以使用符号 "||"。 |
4 |
=A3.groups(DEPT; count(~):Count) |
/分组汇总各部门满足条件的人数 |
A4的执行结果如下:
DEPT |
Count |
4 |
9 |
5 |
5 |
6 |
3 |
7 |
6 |
8 |
1 |
2. 查找最大值 / 最小值对应记录
【例 4】 根据成绩表,求一班数学成绩最低的学生 ID。部分数据如下:
CLASS |
STUDENTID |
SUBJECT |
SCORE |
Class one |
1 |
English |
84 |
Class one |
1 |
Math |
77 |
Class one |
1 |
PE |
69 |
Class one |
2 |
English |
81 |
Class one |
2 |
Math |
80 |
… |
… |
… |
… |
【SPL 脚本】
A |
B |
|
1 |
=connect("db") |
/连接数据库 |
2 |
=A1.query("select * from Scores where SUBJECT='Math'and CLASS='Class one'") |
/查询一班的数学成绩 |
3 |
=A2.minp(SCORE) |
/使用 A.minp() 函数选出最低分所在记录 |
4 |
=A3.STUDENTID |
/从记录中取学生 ID |
最小值所在记录不一定是唯一的,如果想返回所有记录,可以使用函数A.minp()的选项@a :
A |
B |
|
3 |
=A2.minp@a(SCORE) |
/使用 A.minp() 函数的 @a 选项,选出所有最高分的记录 |
4 |
=A3.(STUDENTID) |
/从多条记录中取出学生 ID |
A3的执行结果如下:
CLASS |
STUDENTID |
SUBJECT |
SCORE |
Class one |
5 |
Math |
60 |
Class one |
14 |
Math |
60 |
A4的执行结果如下:
Member |
5 |
14 |
【例 5】 根据奥运会奖牌榜统计表,求总成绩蝉联第一名届数最长的国家。部分数据如下:
Game |
Nation |
Gold |
Silver |
Copper |
30 |
USA |
46 |
29 |
29 |
30 |
China |
38 |
27 |
23 |
30 |
UK |
29 |
17 |
19 |
30 |
Russia |
24 |
26 |
32 |
30 |
Korea |
13 |
8 |
7 |
… |
… |
… |
… |
… |
【SPL 脚本】
A |
B |
|
1 |
=file("Olympic.csv").import@cqt() |
/导入奥运会历届排名 |
2 |
=A1.sort@z(Game, 1000000*Gold+1000*Silver+Copper) |
/按第几届和总成绩降序排列 |
3 |
=A2.group@o1(Game) |
/每届取一名,因为有序也就是第一名 |
4 |
=A3.group@o(Nation) |
/将国家按原序分组 |
5 |
=A4.maxp(~.len()) |
/使用函数 A.maxp() 选出成员数量最多的一组,也就是蝉联次数最多的 |
A5的执行结果如下:
Game |
Nation |
Gold |
Silver |
Copper |
10 |
USA |
41 |
32 |
30 |
9 |
USA |
22 |
18 |
16 |
8 |
USA |
45 |
27 |
27 |
7 |
USA |
41 |
27 |
28 |
3. 分区段查找数据
有时候我们需要通过计算数值在区间中的序号,来获取在集合中的对应成员。比如根据考试成绩返回评价(优、良、中、差);根据家庭年收入返回家庭所处的资产等级(贫困、小康、中产、富裕等)。
【例 6】 根据成绩表,统计英语科目优秀、及格和不及格的人数。部分数据如下:
CLASS |
STUDENTID |
SUBJECT |
SCORE |
Class one |
1 |
English |
84 |
Class one |
1 |
Math |
77 |
Class one |
1 |
PE |
69 |
Class one |
2 |
English |
81 |
Class one |
2 |
Math |
80 |
… |
… |
… |
… |
【SPL 脚本】
A |
B |
|
1 |
=connect("db").query("select * from Scores where SUBJECT='English'") |
/连接数据库,查询英语成绩 |
2 |
=create(Assessment,Score).record(["fail",0,"pass",60,"excellent",90]) |
/创建分数与评价对照表 |
3 |
=A1.derive(A2.segp(Score,SCORE).Assessment:Assessment) |
/使用 A.segp() 函数根据分数在对照表中的区间序号,获取相应的评价。 |
4 |
=A3.groups(Assessment;count(1):Count) |
/按评价分组统计人数 |
A4的执行结果如下:
Assessment |
Count |
excellent |
6 |
fail |
4 |
pass |
18 |
4. 选出前 N 名 / 后 N 名
【例 7】 根据成绩表,求各班各科前两名的学生 ID。部分数据如下:
CLASS |
STUDENTID |
SUBJECT |
SCORE |
Class one |
1 |
English |
84 |
Class one |
1 |
Math |
77 |
Class one |
1 |
PE |
69 |
Class one |
2 |
English |
81 |
Class one |
2 |
Math |
80 |
… |
… |
… |
… |
【SPL 脚本】
A |
B |
|
1 |
=connect("db") |
/连接数据库 |
2 |
=A1.query("select * from Scores") |
/查询学生成绩 |
3 |
=A2.group(CLASS,SUBJECT;~.top(-2;SCORE):TOP2) |
/使用 A.top() 函数获取每个班级各科目分数的前两名。其中 -2 表示从大到小取两名。 |
4 |
=A3.conj(TOP2) |
/将所有班级各科的前两名记录合并到一起 |
A4的执行结果如下:
CLASS |
STUDENTID |
SUBJECT |
SCORE |
Class one |
4 |
English |
96 |
Class one |
9 |
English |
93 |
Class one |
13 |
Math |
97 |
Class one |
10 |
Math |
97 |
… |
… |
… |
… |
5. 根据主键值查找对应记录
根据主键值查找对应记录是很常见的操作,例如根据员工 ID 查找员工记录,根据订单 ID 查询订单详细信息等等。
【例 8】 根据相互关联的课程表和选课表,列出所有学生所选课程信息表,每个科目作为一列。课程表部分数据如下:
ID |
NAME |
TEACHERID |
1 |
Environmental protection and … |
5 |
2 |
Mental health of College Students |
1 |
3 |
Computer language Matlab |
8 |
… |
… |
… |
选课表部分数据如下:
ID |
STUDENT_NAME |
COURSE |
1 |
Rebecca |
2,7 |
2 |
Ashley |
1,8 |
3 |
Rachel |
2,7,10 |
… |
… |
… |
希望转换成如下表格:
ID |
STUDENT_NAME |
COURSE1 |
COURSE2 |
COURSE3 |
… |
1 |
Rebecca |
Mental health of College Students |
Into Shakespeare |
… |
|
2 |
Ashley |
Environmental protection and … |
Modern economics |
… |
|
3 |
Rachel |
Mental health of College Students |
Into Shakespeare |
Music appreciation |
… |
… |
… |
… |
… |
… |
… |
【SPL 脚本】
A |
B |
|
1 |
=connect("db") |
/连接数据库 |
2 |
=A1.query("select * from Course").keys(ID) |
/读取课程表,并设置主键 ID |
3 |
=A1.query("select * from SelectCourse") |
/读取学生选课表 |
4 |
=A3.run(COURSE=COURSE.split@cp()) |
/将选课表中的课程按逗号拆分后赋值给课程字段 |
5 |
=A4.max(COURSE.len()) |
/找到选课最多的数量 |
6 |
=create(ID,STUDENT_NAME, ${A5.("COURSE"+string(~)).concat@c()}) |
/创建空表,课程列按照最多的数量创建 |
7 |
>A4.run(A6.record([ID,STUDENT_NAME]|COURSE.(A2.find(~).Name))) |
/循环选课表,将学生 ID、学生姓名以及使用 A.find() 函数查找出的课程名称合并追加到 A6 的表中。 |
A6的执行结果如下:
ID |
STUDENT_NAME |
COURSE1 |
COURSE2 |
COURSE3 |
1 |
Rebecca |
Mental health of College Students |
Into Shakespeare |
|
2 |
Ashley |
Environmental protection and … |
Modern economics |
|
3 |
Rachel |
Mental health of College Students |
Into Shakespeare |
Music appreciation |
… |
… |
… |
… |
… |
《SPL CookBook》中还有更多相关计算示例。