Operation mysql table cross-database data

Operational transconductance library data table has always been a difficult to say not too difficult, that simple but not simple, in short, is a troublesome thing. A large number of scattered in different data in the database table who obviously feel should merge them together, again a little calculation, it seems that also the case ...... but really do it, it needs to have forgotten the of, but not only is it so going on?
        We want to give these small problems, cut the Gordian knot to a style, whispering sound Jenna clicking permanent solution to it? First of all, you need a sword called a set of solvers (focus); then, you can look at this regarded as buy one get one esoteric knife (free); finally, facing enemies leash ...... you can easily happily to sleep a good night's sleep: the operation of cross-database data table !

1. simply combining (the FROM)

       The so-called cross-database data table logic means are stored with a data table in different databases. The reason may be because the amount of data is too large, difficult to handle in a database, you may need to separate the production library and library history in the business. The different databases, may just be the same kind of database deployment on different machines, it could be even different types of database systems.

       In the face of cross-database data table, especially not the same type of database, the database comes with a tool often beyond the reach of the general need to find third-party tools can support multiple data source types, and set solver, it can be said to be one of the best up. Here, we have a mixed situation for the operation of several common database across a detailed discussion about:

       Cross-database operation, simple and crude idea is to spread the same logical respective library data table merged into a table, and then to operate on this table.

       For example, in the two databases and the MYSQL HSQL respectively stored a student transcripts, both to save a part of each student information, as shown below:

       

       

       Using a resolver set, we can easily these same two tables into one table structure, set solvers SPL script as follows: 

 

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 student achievement table")

= B1.query ( "select * from student achievement table")

3

=A2 | B2

       A1, A2 and B1, B2 respectively read two student transcript library, and A3 in a simple and intuitive way to put the two tables merged.

       In this way the two tables are actually read into the memory, it generates a sequence tables resolver object set and then using the operation sequence table "|" to complete the merger. Some students may ask: if my data than larger, can not be read into memory how to do? It does not matter, is designed to handle large data sets born solvers, will never be stumped by such a simple little question. We can use the cursor, the same can achieve rapid splicing table:

 

A

B

2

= A1.cursor ( "select * from student achievement table")

= B1.cursor ( "select * from student achievement table")

3

=[A2, B2] .conjx()

       A2, B2, respectively, with two cursors open student achievement library table, A3 use conjx () function of the two cursors merge, forming a new access to two tables at the same time cursors.

       Corresponding to SQL, which you simply combining like just finished from the work, so that the same data across the database table structure "longitudinal" splicing into a sequence tables can be accessed or cursor, but the actual calculation, also relates to filter (WHERE / having), packet aggregation (group + sum / count / avg / max / min), the connection (join + on), de-emphasis (distinct), sorting (order), taking part of the data (limit + offset), etc. operations , here we will discuss these operations start one by one.

       Of course, we deal with the needs of these operations, not simply realize the function, we also need to consider the efficiency and performance to achieve, so in principle, we will try to take advantage of the computing power of the database, and set count is mainly responsible for the mixing operation. Sometimes, however, also need to be responsible for almost all of the operations set by the operator, databases only responsible for storing data.

 

2.    WHERE

       where the nature of the filter by comparing the calculation result of the removal of the comparison is false, the recording, where thus act only on one record, does not involve the operation between the recording does not need to consider the data which is located in the database. For example, in the previous example, we want to count the "class" all students "math" scores, a single library SQL like this:

       SELECT student ID, student achievement scores FROM table WHERE Subject = 'math' AND class = 'class'

       When the multi-database, as long as the where clause written directly in SQL, a database to allow parallel processing of each filter can be:

 

A

B

2

= A1.query ( "select student ID, student achievement scores from the table where subjects = 'math' and class = 'class'')

= B1.query ( "select student ID, student achievement scores from the table where subjects = 'math' and class = 'class'')

3

=A2 | B2

       We can also make set solver is responsible for all filter operation, the database only stores data. Then you can use the solver select function set (with a different SQL select keywords)

 

A

B

2

= A1.query ( "select student ID, grades, subjects, classes from student achievement table")

= B1.query ( "select student ID, grades, subjects, classes from student achievement table")

3

= A2.select (Subject == "Mathematics" && class == "class") .new (student ID, score)

= B2.select (Subject == "Mathematics" && class == "class") .new (student ID, score)

4

= A3 | B3

       When a large amount of data, the same sequence tables may be replaced with a cursor, use the function to connect conjx:

 

A

B

2

= A1.cursor ( "select student ID, student achievement scores from the table where subjects = 'math' and class = 'class'')

= B1.cursor ( "select student ID, student achievement scores from the table where subjects = 'math' and class = 'class'')

3

=[A2, B2].conjx()

 

3. ORDER BY and LIMIT OFFSET

       order by a process carried out only after the result set is generated. In the above example, if we Yaoan math ordering, for a single database, only need to add the order by clause:

       SELECT class, student ID, student achievement scores FROM table WHERE Subject = 'math' AND class = 'class' ORDER BY score

       For more than a database, you can let the database are first sorted and then merged ordered data set by the calculator. This can play a maximum performance parallel database server.

 

A

B

2

= A1.query ( "select class, student ID, student achievement scores from the table where subjects = 'math' and class = 'class' order by score")

= B1.query ( "select class, student ID, student achievement scores from the table where subjects = 'math' and class = 'class' order by score")

3

= [A2, B2] .merge (Results)

       Can reverse order, when added before the merge sort field "-" (merge function can not add "-", but according to the standard wording is added)

 

A

B

2

= A1.query ( "select class, student ID, student achievement scores from the table where subjects = 'math' AND class = 'class' order by score desc")

= B1.query ( "select class, student ID, student achievement scores from the table where subjects = 'math' AND class = 'class' order by score desc")

3

= [A2, B2] .merge (- Results)

       Of course, you can be completely set by a sort solver:

 

A

B

2

= A1.query ( "select class, student ID, student achievement scores from the table where subjects = 'math' AND class = 'class'')

= B1.query ( "select class, student ID, student achievement scores from the table where subjects = 'math' AND class = 'class'')

3

=[A2.sort( 成绩),   B2.sort(成绩)].merge(成绩)

       由集算器实现倒序排序:

 

A

B

2

=A1.query("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND   班级 =' 一班 '")

=B1.query("select   班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND 班级 =' 一班 '")

3

=[A2.sort(- 成绩), B2.sort(- 成绩)].merge(- 成绩)

       而对于大数据量,需要使用游标及 mergex 来完成有序归并:

 

A

B

2

=A1.cursor("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND   班级 =' 一班 'order by 成绩")

=B1.cursor("select   班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND 班级 =' 一班 'order by 成绩")

3

=[A2 , B2].mergex(成绩)

       limit 和 offset 的执行又在 order 之后,例子中如果想取数学成绩除了第一名之后的前十名(可以少于但不能多于),单库情况下 SQL 是这样的:

       SELECT 班级, 学生 ID, 成绩 FROM 学生成绩表 WHERE 科目 =’数学’ AND 班级 =‘一班’ ORDER BY 成绩 DESC LIMIT 10 OFFSET 1

       多数据库时,可以用集算器的 to 函数实现 limit offset 的功能,to(n+1,n+m) 等同于 limit m offset n

 

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(- 成绩).to(2, 11)

       对于大数据量使用游标的情况,offset 功能可以使用集算器函数 skip 实现,而 limit 的功能则可以使用函数 fetch 实现

 

A

B

2

=A1.cursor("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND   班级 =' 一班 'order by 成绩 desc")

=B1.cursor("select   班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND 班级 =' 一班 'order by 成绩 desc")

3

=[A2, B2].mergex(- 成绩).

=A3.skip(1)

4

=A3.fetch(10)

 

 

4.    聚合运算

       我们来讨论五种常见的聚合运算:sum/count/avg/max/min。

       •   sum 的基础是加法,根据加法结合律,各数据库中内部数据先分别求和,然后拼接成一张表后再求总和,与先拼接成一张表然后一起求和的结果,其实是一样的。

       •   count 的本质,是对每项非 null 数据计 1,null 数据计 0,然后进行累加计算。所以其本质仍是加法运算,与 sum 一样符合加法结合律。唯一不同的是对原始数据不是累加其本身的数值而是计 1(非 null)或计 0(为 null)。

       •   avg 的本质,是当 count > 0 时 avg = sum/count,当 count = 0 时 avg = null。显然 avg 不能像 sum 或 count 那样先分别计算了。不过根据定义,我们可以先算出 sum 和 count,再通过 sum 和 count 计算出 avg。

       •   max 和 min 的基础都是比较运算,而因为比较运算具有传递性,因此所有数据库的最值,可以通过比较各个数据库的最值得到。

       依旧是上面的例子,这次我们要求两个班全体学生的数学总分、人数、平均分、最高及最低分,对于单源数据:

       SELECT sum(成绩) 总分数, count(成绩) 考试人数, avg(成绩) 平均分, max(成绩) 最高分, min(成绩) 最低分 FROM 学生成绩表 WHERE 科目 ='数学'

       聚合运算的结果集很小,只有一行,因此无论源数据量的大小,都可以使用游标,代码如下:

 

A

B

2

=A1.cursor("select sum( 成绩) 总分数, count(成绩) 考试人数, max(成绩) 最高分,   min(成绩) 最低分 from 学生成绩表 where 科目 =' 数学 ' ")

=B1.cursor("select   sum( 成绩) 总分数, count(成绩) 考试人数, max(成绩) 最高分, min(成绩) 最低分 from   学生成绩表 where 科目 =' 数学 ' ")

3

=[A2, B2].conjx().total(sum( 总分数), sum(考试人数), max(最高分), min(最低分))

4

=create(总分数, 考试人数, 平均分, 最高分, 最低分).insert(0, A3(1), A3(2), if(A3(2)!=0,A3(1)/A3(2),null), A3(3),   A3(4))

       事实上,前面提到的 order by +limit offset 本质上也可以看成是一种聚合运算:top。从这个角度进行优化,可以获得更高的计算效率。毕竟数据量大时,全排序的成本很高,而且取前 N 个数据的操作也并不需要全排序。当然,这个方法对于数据量小的情况也同样适用。

       具体来说,对于 order by F limit m offset n 的情况,只需先用 top(n+m, F, ~),再用 to(n+1,) 就行了。

       我们仍以之前的含 order by+limit offset 的 SQL 语句为例:

       SELECT 班级, 学生 ID, 成绩 FROM 学生成绩表 WHERE 科目 =’数学’ AND 班级 =‘一班’ ORDER BY 成绩 DESC LIMIT 10 OFFSET 1

       对于多数据库, 脚本如下,其中倒序排序只需在排序字段前加“-”:

 

A

B

2

=A1.cursor("select 班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND   班级 =' 一班 '")

=B1.cursor("select   班级, 学生 ID, 成绩 from 学生成绩表 where 科目 =' 数学 'AND 班级 =' 一班 '")

3

=[A2, B2].conjx().top(11, - 成绩, ~).to(2,)

 

 

5.    GROUP BY、DISTINCT 和 HAVING

       A、分组聚合运算

       对于 group by,因为最终所得结果与样本个体的输入顺序无关,所以只要样本的总体不变,最终结果也不会变。也就是说,只要在从分库中提取数据和最终汇总全部数据时,都预先进行了分类运算即可。

       假设我们想分别求一、二班的数学总分、人数、平均分、最高及最低分,单数据库如下:

       SELECT 班级, sum(成绩) 总分数, count(成绩) 考试人数, avg(成绩) 平均分, max(成绩) 最高分, min(成绩) 最低分 FROM 学生成绩表 WHERE 科目 ='数学' GROUP BY 班级

       我们分三种情况讨论:

       第一,对于小数据,聚合运算的结果集只会更小,这时推荐使用 query+groups:

 

A

B

2

=A1.query("select 班级, sum( 成绩) 总分数,   count(成绩) 考试人数, max(成绩) 最高分, min(成绩) 最低分 from 学生成绩表 where 科目 =' 数学 'group by 班级")

=B1.query("select   班级, sum( 成绩) 总分数, count(成绩) 考试人数,   max(成绩) 最高分, min(成绩) 最低分 from 学生成绩表   where 科目 =' 数学 'group by 班级")

3

=(A2 | B2) .groups(班级: 班级; sum( 总分数): 总分数, sum(考试人数): 考试人数,   max(最高分): 最高分, min(最低分): 最低分 )

4

=A3.new(班级, 总分数, 考试人数, if( 考试人数 ==0,   null, 总分数 / 考试人数): 平均分, 最高分, 最低分 )

       第二,对于大数据量,如果结果集也很大,那么就应该使用 cursor+groupx。

       另外,由于大结果集的分组计算较慢,需要在外存产生缓存数据。而如果我们在数据库中对数据先排序,则可以避免这种缓存(此时计算压力会转到数据库,因此需要根据实际情况权衡,通常情况下,数据库服务器的计算能力会更强一些)。

       具体的办法是对 SQL 的结果集使用 order by 排序,然后在集算器中使用 mergex 函数归并后,再使用 groupx 的 @o 选项分组:

 

A

B

2

=A1.cursor("select   班级, sum( 成绩) 总分数, count(成绩) 考试人数,   max(成绩) 最高分, min(成绩) 最低分 from 学生成绩表   where 科目 =' 数学 'group by 班级   order by 班级")

=B1.cursor("select 班级, sum( 成绩) 总分数,   count(成绩) 考试人数, max(成绩) 最高分, min(成绩) 最低分 from 学生成绩表 where 科目 =' 数学 'group by 班级 order   by 班级")

3

=[A2   , B2].mergex(班级).groupx@o(班级: 班级; sum( 总分数): 总分数,   sum(考试人数): 考试人数, max(最高分): 最高分, min(最低分): 最低分 )

4

=A3.new(班级, 总分数, 考试人数, if( 考试人数 ==0, null, 总分数 / 考试人数): 平均分, 最高分, 最低分 )

       当然如果不希望加重数据库负担,也可以让数据库只做分组而不排序,此时集算器直接用 groupx,注意不能加 @o 选项。另外汇总数据时,也要把 mergex 换成 conjx:

 

A

B

2

=A1.cursor("select 班级, sum( 成绩) 总分数,   count(成绩) 考试人数, max(成绩) 最高分, min(成绩) 最低分 from 学生成绩表 where 科目 =' 数学 'group by 班级")

=B1.cursor("select   班级, sum( 成绩) 总分数, count(成绩) 考试人数,   max(成绩) 最高分, min(成绩) 最低分 from 学生成绩表   where 科目 =' 数学 'group by 班级")

3

=[A2, B2].conjx().groupx( 班级: 班级; sum( 总分数): 总分数, sum(考试人数): 考试人数,   max(最高分): 最高分, min(最低分): 最低分 )

4

=A3.new(班级, 总分数, 考试人数, if( 考试人数 ==0,   null, 总分数 / 考试人数): 平均分, 最高分, 最低分 )

       第三,如果已明确地知道结果集很小,那么推荐用 cursor+groups

       此时 groups 比 groupx 有更好的性能,因为 groups 将运算数据都保存在内存中,比 groupx 节省了写入外存文件的时间。

       另外用 groups 可以不要求在数据库中预先排序,因为数据库 group by 的结果集本身不一定有序,再使用 orde by 排序也会增加成本。而对于小结果集,集算器用 groups@o 也并不一定比直接用 groups 更有效率。

       通常,汇总数据要用 conjx

 

A

B

2

=A1.cursor("select 班级, sum( 成绩) 总分数,   count(成绩) 考试人数, max(成绩) 最高分, min(成绩) 最低分 from 学生成绩表 where 科目 =' 数学 'group by 班级")

=B1.cursor("select   班级, sum( 成绩) 总分数, count(成绩) 考试人数,   max(成绩) 最高分, min(成绩) 最低分 from 学生成绩表   where 科目 =' 数学 'group by 班级")

3

=[A2, B2].conjx().groups( 班级: 班级; sum( 总分数): 总分数, sum(考试人数): 考试人数,   max(最高分): 最高分, min(最低分): 最低分 )

4

=A3.new(班级, 总分数, 考试人数, if( 考试人数 ==0,   null, 总分数 / 考试人数): 平均分, 最高分, 最低分 )

       B、去重后计数 (count distinct)

       在各个数据库内去重,可以使用 distinct 关键字。而数据库之间的数据去重,则可以使用集算器的 merge@u 函数。要注意的是使用前应该确保表内数据对主键字段(或者具有唯一性的一个或多个字段)有序。

       对于 distinct 来说, sum(distinct)、avg(distinct) 的计算方法与 count(distinct) 大同小异,而且业务中不常用到,而 max(distinct)、min(distinct) 与单纯使用 max、min 没有区别。因此,我们只以 count(distinct) 为例加以说明。

       比如,想要计算全年级(假设只有一班和二班)语数外三科至少有一科不及格需要补考的总人数,单数据库的 SQL 是这样的:

       SELECT count(distinct 学生 ID) 人数 FROM 学生成绩表 WHERE 成绩 <60

       对于多源数据,全分组聚合在使用游标或序表方面没有差别,为了语法简便起见以游标为例:

 

A

B

2

=A1.cursor("select distinct 班级, 学生 ID from 学生成绩表   where 成绩 <60 order by 班级, 学生 ID")

=B1.cursor("select   distinct 班级, 学生 ID from 学生成绩表 where 成绩 <60 order by 班级, 学生 ID")

3

=[A2, B2].mergex@u(班级, 学生 ID).total(count( 学生 ID))

       再如,想要分别计算每班语数外三科至少有一科不及格需要补考的总人数,单数据库的 SQL 是这样的:

       SELECT 班级, count(distinct 学生 ID) 人数 FROM 学生成绩表 WHERE 成绩 <60 GROUP BY 班级

       对于多数据库,同样需要先汇总去重,再进行分组聚合。汇总前需要数据有序,且汇总后数据仍然有序,所以分组函数 groups 和 groupx 都可以使用 @o 选项。

       对于小数据量,可以使用 merge@u、groups@o 和 query:

 

A

B

2

=A1.query("select distinct 班级, 学生 ID from 学生成绩表   where 成绩 <60 order by 班级, 学生 ID")

=B1.query("select   distinct 班级, 学生 ID from 学生成绩表 where 成绩 <60 order by 班级, 学生 ID")

3

=[A2, B2].merge@u(班级, 学生 ID) .groups@o(班级: 班级; count( 学生 ID): 补习人数 )

       对于大数据量小结果集,可以使用 mergex@u、groups@o 和 cursor:

 

A

B

2

=A1.cursor("select distinct 班级, 学生 ID from 学生成绩表   where 成绩 <60 order by 班级, 学生 ID")

=B1.cursor("select   distinct 班级, 学生 ID from 学生成绩表 where 成绩 <60 order by 班级, 学生 ID")

3

=[A2, B2].mergex@u(班级, 学生 ID) .groups@o(班级: 班级; count( 学生 ID): 补习人数 )

       对于大数据量大结果集,可以使用 mergex@u、groupx@o 和 cursor:

 

A

B

2

=A1.cursor("select distinct 班级, 学生 ID from 学生成绩表   where 成绩 <60 order by 班级, 学生 ID")

=B1.cursor("select   distinct 班级, 学生 ID from 学生成绩表 where 成绩 <60 order by 班级, 学生 ID")

3

=[A2, B2].mergex@u(班级, 学生 ID) .groupx@o(班级: 班级; count( 学生 ID): 补习人数 )

       C、对聚合字段过滤(having)

       having 是对聚合 (分组) 后得出的结果集再做过滤。所以当语句中有 having 出现时,如果聚合 (分组) 操作没有彻底执行完毕,需要将 having 子句先提取出来。待数据彻底完成聚合 (分组) 操作之后,再执行条件过滤。

       对于多源数据,如果聚合计算是在汇总之后才能最终完成,那么 having 必须使用集算器的函数 select 来实现过滤。

       下面主要说明这种聚合计算在汇总之后才完成的情况:比如,想要获得一班和二班的三个科目的考试中,有哪些平均分是低于 60 分的。对于单数据库,SQL 可以这样写:

SELECT 班级, 科目, avg(成绩) 平均分 FROM 学生成绩表 GROUP BY 班级, 科目 HAVING avg(成绩)<60

       对于多数据库,相关集算器执行代码如下:

 

A

B

2

=A1.query("select 班级, 科目, sum( 成绩) 总分, count(成绩) 人数   from 学生成绩表 group by 班级, 科目")

=B1.query("select   班级, 科目, sum( 成绩) 总分, count(成绩) 人数 from 学生成绩表 group by 班级, 科目")

3

=(A2 | B2).groups(班级: 班级, 科目: 科目; sum( 总分): 总分, sum(人数): 人数 ).new( 班级, 科目, ( 总分 / 人数): 平均分 )

4

=A3.select(平均分 <60)

       对于大数据量,需要使用游标 (select 函数同样适用于游标)

 

A

B

2

=A1.cursor("select 班级, 科目, sum( 成绩) 总分, count(成绩) 人数   from 学生成绩表 group by 班级, 科目 order by 班级, 科目")

=B1.cursor("select   班级, 科目, sum( 成绩) 总分, count(成绩) 人数 from 学生成绩表 group by 班级, 科目 order by 班级, 科目")

3

=[A2, B2].mergex(班级, 科目).groupx@o(班级: 班级, 科目: 科目; sum( 总分): 总分, sum(人数): 人数 ).new( 班级, 科目, ( 总分 / 人数): 平均分 )

4

=A3.select(平均分 <60)

 

6.    JOIN ON

       跨库的 JOIN 实现起来非常困难,不过比较幸运的是,我们可以通过存储设计避免很多跨库 JOIN。我们分三种情况讨论:

       1. 同维表分库,需要重新拼接为一个表

       2. 要连接的外键表在每个库中都有相同的一份

       3. 需要连接的外键表在另一个库中

       对于集算器来讲,前两种的处理情况是一样的:都不需要涉及跨库 join,join 操作都可以在数据库内完成。区别只在于第一种是分库表,数据库之间没有重复数据;而第二种则要求把外键表的数据复制到每个库中。

       如果外键表没有复制到每个库中,那就会涉及真正的跨库 join,因为很复杂,这里只举一个内存外键表的例子,其它更复杂情况会有专门的文章阐述。

       A、同维表或主子表同步分库

       所谓同维表,简单来讲就是两个表的主键字段完全一样,且其中一个表的主键与另一个表的主键有逻辑意义上的外键约束(并不要求数据库中一定有真正的外键,主键同理也是逻辑上的主键并不一定存在于数据库中)。

       假设有两个库,每个库中有两个表,分别记为 A 库中的 A1 表和 A2 表,B 库中的 B1 表和 B2 表。从逻辑上看 1 表是 A1 表加上 B1 表,2 表是 A2 表加上 B2 表,我们再假设 1 表与 2 表为同维表,现在要做 1 表与 2 表的 join 连接运算。

       所谓同步分库,就是在设计分库存储时,保证了 1 表和 2 表按主键进行了同步的分割。也就是必须保证分库之后,A1 和 B2 的 join 等值连接的结果是空集,同样 A2 和 B1 的 join 等值连接的结果也是空集,这样也就不必有跨库的 join 连接运算了。

       举例说明,比如有两张表:股票信息与公司信息,表的结构如下:

       公司信息

       

       股票信息

       

       两个表的主键都是 (公司代码, 股票代码),且股票信息的主键与公司信息的主键有逻辑意义上的外键约束关系,二者互为同维表。

       现在假设我想将两个表拼接在一起,单数据库时 SQL 是这样的:

       SELECT * FROM 公司信息 T1 JOIN 股票信息 T2 ON T1. 公司代码 =T2. 公司代码 AND T1. 股票代码 = T2. 股票代码

       现假设公司信息分为两部分,分别存于 HSQL 和 MYSQL 数据库中,股票信息同样分为两部分,分别存于 HSQL 和 MYSQL 数据库中,且二者是同步分库。

       join 连接公司信息与股票信息的集算器代码:

 

A

B

2

=A1.query("SELECT * FROM 公司信息 T1 JOIN 股票信息 T2 ON T1. 公司代码 =T2. 公司代码 AND T1. 股票代码 = T2. 股票代码")

=B1.query("SELECT * FROM 公司信息 T1 JOIN 股票信息 T2 ON T1. 公司代码 =T2. 公司代码 AND T1. 股票代码 = T2. 股票代码")

3

=A2 | B2

       对于大数据:

 

A

B

2

=A1.cursor("SELECT * FROM 公司信息 T1 JOIN 股票信息 T2 ON T1. 公司代码 =T2. 公司代码 AND T1. 股票代码 = T2. 股票代码")

=B1.cursor("SELECT * FROM 公司信息 T1 JOIN 股票信息 T2 ON T1. 公司代码 =T2. 公司代码 AND T1. 股票代码 = T2. 股票代码")

3

=[A2,   B2].conjx()

       主子表的情况与同维表类似,即一个表(主表)的主键字段被另一个表(子表)的主键字段所包含,且子表中对应的主键字段与主表的主键有逻辑意义上的外键约束关系。

       举例说明,比如有两张表:订单与订单明细,表的结构如下:

       订单

       

       订单明细

       

       其中订单是主表,主键为 (订单 ID);而订单明细为子表,主键为 (订单 ID, 产品 ID),且订单明细的主键字段订单 ID,与订单的主键有逻辑意义上的外键约束关系,显然二者为主子表的关系。

       现在假设我想将两个表拼接在一起,单数据库的 SQL 是这样的:

       SELECT * FROM 订单 T1 JOIN 订单明细 T2 ON T1. 订单 ID=T2. 订单 ID

       现假设订单分为两部分,分别存于 HSQL 和 MYSQL 数据库中,订单明细同样分为两部分,分别存于 HSQL 和 MYSQL 数据库中,且二者同步分库。

       join 连接订单与订单明细的集算器代码:

 

A

B

2

=A1.query("SELECT * FROM 订单 T1 JOIN 订单明细 T2 ON T1. 订单 ID=T2. 订单 ID")

=B1.query("SELECT * FROM 订单 T1 JOIN 订单明细 T2 ON T1. 订单 ID=T2. 订单 ID")

3

=A2 | B2

       对于大数据:

 

A

B

2

=A1.cursor("SELECT * FROM 订单 T1 JOIN 订单明细 T2 ON T1. 订单 ID=T2. 订单 ID")

=B1.cursor("SELECT * FROM 订单 T1 JOIN 订单明细 T2 ON T1. 订单 ID=T2. 订单 ID")

3

=[A2,   B2].conjx()

       B、外键表复制进每个库

       所谓外键表,即是指连接字段为外键字段的情况。这种外键表 join 也是业务上常见的一种情况。因为要连接的外键表在每个库中都有同一份,那么两个外键表汇总并去重后,其实还是任一数据库中原来就有的那个外键表。

       而 join 的连接操作,本质上可以视为一种乘法,因为 join 连接等价于 cross join 后再用 on 中条件进行过滤。则根据乘法分配率可以推导出:若是需要做连接操作的外键表(不妨设为连接右侧的表)在每个库中都有同一份,则连接左侧的表(每个数据库中各有其一部分)在汇总后再连接,等同于各数据中的连接左侧的表与外键表先做连接操作后,再汇总到一起的结果。如图所示:

       

       所以我们在存储设计时,只要在每个数据库中把外键表都重复一下,就可以避免复杂的跨库 join 操作。一般情况下,外键表作为维表的数据量相对较小,这样重复的成本就不会很高,而事实表则会得很大,然后用分库存储的方法,来解决运算速度缓慢或存储空间不足等问题。

       例如,有两个表:客户销售表和客户表,其中客户销售表的外键字段:客户,与客户表的主键字段:客户 ID,有外键约束关系。现在我们想查询面向河北省各公司的销售额记录,对于单数据源,它的 SQL 是这样写的:

       SELECT T1. 公司名称 公司名称, T2. 订购日期 订购日期, T2. 销售额 销售额 FROM 客户表 T1 JOIN 客户销售表 T2 ON T1. 客户 ID=T2. 客户 WHERE T1. 省份 ='河北'

       对于多数据源的情况,我们假设客户销售表分别存储在两个不同的数据库中,而每个数据库中都有同一份的客户表做为外键表。则相关的集算器代码如下:

 

A

B

2

=A1.query("select T1. 公司名称 公司名称, T2. 订购日期 订购日期, T2. 销售额 销售额 from 客户表 T1 join 客户销售表 T2 on T1. 客户 ID=T2. 客户 where T1. 省份 =' 河北 ' ")

=B1.query("select   T1. 公司名称 公司名称, T2. 订购日期 订购日期,   T2. 销售额 销售额 from 客户表 T1 join 客户销售表 T2 on T1. 客户 ID=T2. 客户 where T1. 省份 =' 河北 ' ")

3

=A2 | B2

       大数据量使用游标时:

 

A

B

2

=A1.cursor("select T1. 公司名称 公司名称, T2. 订购日期 订购日期, T2. 销售额 销售额 from 客户表 T1 join 客户销售表 T2 on T1. 客户 ID=T2. 客户 where T1. 省份 =' 河北 ' ")

=B1.cursor("select   T1. 公司名称 公司名称, T2. 订购日期 订购日期,   T2. 销售额 销售额 from 客户表 T1 join 客户销售表 T2 on T1. 客户 ID=T2. 客户 where T1. 省份 =' 河北 ' ")

3

=[A2, B2].conjx()

       C、需要连接的外键表在另一个库中

       对于维表(外键表)也被分库的情况,我们只考虑维表全部可内存化的情况,不可内存化时,常常就不适合再将数据存在数据库中了,需要专门针对性的的存储和计算方案,这将在另外的文章中专门讨论。在这里我们只通过例子来讨论维表可内存化的情况。

       对于这种情况,当涉及的数据量比较大而需要使用游标时,计算逻辑会变得比较复杂。所以我们在这里只讲一下针对小数据量的使用序表的 join 处理方法。关于对大数据量的使用游标的 join 处理,会另有一篇文章做专门的介绍。

       当要做 join 连接运算的外键表全部或部分存储在另一个库中时,最直观的办法就是将两个表都提取出来并各自汇总后,再计算 join 连接。

       下面仍以客户销售表和客户表来举例,假设外键表客户表也分别存储在两个数据库中,此时就不能在 SQL 中使用 join 关键字来实现连接运算了,但我们可以将其提取出来后,用集算器的 join 函数来实现目的,它的集算器代码如下所示:

 

A

B

2

=A1.query("select 客户 ID 客户, 公司名称 from 客户表   where 省份 =' 河北 ' ")

=B1.query("select   客户 ID 客户, 公司名称 from 客户表 where 省份 =' 河北 ' ")

3

=A2 | B2

4

=A1.query("select 客户, 订购日期, 销售额 from 客户销售表")

=B1.query("select 客户, 订购日期, 销售额 from 客户销售表")

5

=A4 | B4

6

=join(A3: 客户表, 客户; A5: 客户销售表, 客户)

7

=A4.new(~.field(1).field(1): 客户, ~.field(1).field(2): 公司名称, ~.field(2).field(2): 订购日期,   ~.field(2).field(3): 销售额 )

       当事实表数据量较大的时候,也可以使用游标处理事实表,只需将 join 换成 cs.join 即可:

 

A

B

2

=A1.query("select 客户 ID 客户, 公司名称 from 客户表   where 省份 =' 河北 ' ")

=B1.query("select   客户 ID 客户, 公司名称 from 客户表 where 省份 =' 河北 ' ")

3

=A2 | B2

4

=A1.cursor("select 客户, 订购日期, 销售额 from 客户销售表")

=B1.cursor("select 客户, 订购日期, 销售额 from 客户销售表")

5

=[A4, B4].conjx()

6

=A5.join(客户, A3: 客户)

 

7.    简单 SQL

       前面我们主要是从计算原理的角度出发,分析了如何使用集算器实现类似 SQL 效果的多数据源混合计算。除此之外,集算器还提供了一种更简单、直观的方法,那就是可以在各个数据库上通过 SQL 查询获取游标,用所有这些游标构建成一个多路游标对象,再用简单 SQL 对这个多路游标做二次处理。如果简单 SQL 中没有涉及 join 的运算,甚至还可以让集算器直接将一句简单 SQL 翻译成各种数据库的 SQL,从而实现更进一步的自动化。不过这种办法属于比较保守的做法,虽然简单直接,但不能利用所了解的数据情况进行优化(比如不会使用 groups),因此性能就会差一些。

       下面仍旧用学生成绩的例子,我们想要计算每个班的数学成绩的总分、考试人数、平均分、最高分和最低分,使用简单 SQL 处理这个问题的集算器代码如下:

 

A

B

2

=A1.cursor("select 班级, 成绩 from 学生成绩表   where 科目 =' 数学 ' ")

=B1.cursor("select   班级, 成绩 from 学生成绩表 where 科目 =' 数学 ' ")

3

=[A2, B2].mcursor()

4

=connect().query("select 班级, sum( 成绩) 总分,   count(成绩) 考生人数, avg(成绩) 平均分, max(成绩) 最高分, min(成绩) 最低分 from   {A3} group by 班级")

       因为使用了游标,所以这种写法也可以用于大数据量。另外再提一句,这个办法甚至也可以用于非数据库的数据源(比如文件数据源)!简单 SQL 的特性可参考相关文档,这里就不再进一步举例了。

Guess you like

Origin www.cnblogs.com/HKROnline-SyncNavigator/p/10971489.html