1. 简单合并(FROM)
所谓跨库数据表,是指逻辑上同一张数据表被分别存储在不同数据库中。其原因有可能是因为数据量太大,放在一个数据库难以处理,也可能在业务上就需要将生产库和历史库分开。而不同的数据库,可能只是部署在不同的机器上的同种数据库,也可能是连类型都不同的数据库系统。
在面对跨库数据表,特别是数据库类型都不相同的情况时,数据库自带的工具往往就力所不及了,一般都需要寻找能够很好地支持多数据源类型的第三方工具,而集算器,可以说是其中的佼佼者了。下面,我们就针对几种常见的跨库混合运算情况详细讨论一下:
跨库运算,简单粗暴的思路就是把散布在各个库里的逻辑上相同的数据表合并成一个表,然后在这一个表上进行运算。
例如,在两个数据库 HSQL 和 MYSQL 中,分别存储了一张学生成绩表,两者各自保存了一部分学生信息,如下图所示:
利用集算器,我们可以很容易地将这两个结构相同的表合并为一个表,集算器的 SPL 脚本如下:
A |
B |
|
1 |
=connect("org.hsqldb.jdbcDriver","jdbc:hsqldb:hsql://127.0.0.1/demo?user=sa") |
=connect("com.mysql.jdbc.Driver","jdbc:mysql://127.0.0.1:3306/demo?user=root&password=password") |
2 |
=A1.query("select * from 学生成绩表") |
=B1.query("select * from 学生成绩表") |
3 |
=A2 | B2 |
A1、A2 和 B1、B2 分别读取了两个库里的学生成绩表,而 A3 用一种简单直观的方式就把两个表合并了。
这种方式实际上是把两个表都读入了内存,分别生成了集算器的序表对象,然后利用序表的运算“|”完成了合并。可能有的同学会问:如果我的数据量比较大,无法全部读入内存怎么办?没关系,专为处理大数据而生的集算器,决不会被这么简单的小问题难住。我们可以使用游标,同样可以实现表的快速拼接:
A |
B |
|
2 |
=A1.cursor("select * from 学生成绩表") |
=B1.cursor("select * from 学生成绩表") |
3 |
=[A2, B2] .conjx() |
A2、B2 分别用游标打开两个库里的学生成绩表,A3 则使用 conjx() 函数将这两个游标合并,形成了一个新的可以同时访问两个表的游标。
对应于 SQL,这种简单合并好比只是完成了 from 工作,让结构相同的跨库表的数据“纵向”拼接成了一个可以访问的序表或者游标,而实际运算中,还会涉及过滤 (where/having)、分组聚合 (group+sum/count/avg/max/min)、连接 (join+on)、去重 (distinct)、排序 (order)、取部分数据 (limit+offset),等等操作,下面我们就将对这些运算一一展开讨论。
当然,我们在处理这些运算的需求时,不能只是简单的实现功能,我们还需要考虑实现的效率和性能,因此原则上,我们会尽量利用数据库的计算能力,而集算器主要负责混合运算。不过,有时也需要由集算器负责几乎所有的运算,数据库仅仅负责存储数据。
2. WHERE
where 过滤的本质是通过比较计算,去除比较的结果是 false 的记录,因此 where 只作用于一条记录,不涉及记录之间的运算,也不需要考虑数据位于哪个数据库。比如,在前面的例子中,我们要统计出“一班”所有同学的“数学”成绩,单库中的 SQL 是这样的:
SELECT 学生 ID, 成绩 FROM 学生成绩表 WHERE 科目 =’数学’ AND 班级 =‘一班’
多库时,也只要将 where 子句直接写在 SQL 中,让各个数据库去并行处理过滤就可以了:
A |
B |
|
2 |
=A1.query("select 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'and 班级 =' 一班 ' ") |
=B1.query("select 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'and 班级 =' 一班 ' ") |
3 |
=A2 | B2 |
我们也可以让集算器负责所有过滤运算,数据库仅存储数据。这时可以使用集算器的 select 函数(与 SQL 的 select 关键字不同)
A |
B |
|
2 |
=A1.query("select 学生 ID, 成绩, 科目, 班级 from 学生成绩表") |
=B1.query("select 学生 ID, 成绩, 科目, 班级 from 学生成绩表") |
3 |
=A2.select(科目 =="数学" && 班级 =="一班").new(学生 ID, 成绩) |
=B2.select(科目 =="数学" && 班级 =="一班").new(学生 ID, 成绩) |
4 |
=A3 | B3 |
数据量较大时,同样也可以将序表换成游标,使用 conjx 函数进行连接:
A |
B |
|
2 |
=A1.cursor("select 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'and 班级 =' 一班 ' ") |
=B1.cursor("select 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'and 班级 =' 一班 ' ") |
3 |
=[A2, B2].conjx() |
3. ORDER BY 和 LIMIT OFFSET
order by 是在结果集产生后才进行的处理。在上面的例子中,如果我们要按数学成绩排序,对于单数据库,只需要加上 order by 子句:
SELECT 班级, 学生 ID, 成绩 FROM 学生成绩表 WHERE 科目 =’数学’ AND 班级 =‘一班’ ORDER BY 成绩
而对于多数据库,可以让数据库先分别排序,然后由集算器归并有序数据。这样可以最大的发挥数据库与并行服务器的性能。
A |
B |
|
2 |
=A1.query("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'and 班级 =' 一班 'order by 成绩") |
=B1.query("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'and 班级 =' 一班 'order by 成绩") |
3 |
=[A2, B2].merge(成绩) |
也可以倒序排序,归并时在排序字段前加“-”(merge 函数可以不加“-”,不过按标准写法是加上的)
A |
B |
|
2 |
=A1.query("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND 班级 =' 一班 'order by 成绩 desc") |
=B1.query("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND 班级 =' 一班 'order by 成绩 desc") |
3 |
=[A2, B2].merge(- 成绩) |
当然也可以完全由集算器来排序:
A |
B |
|
2 |
=A1.query("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND |