一、algo是什么:
1、Algo是内存数据库计算引擎,提供SQL计算功能,支持常用的SQL算法,遵循SQL92标准 ,是一个迭代计算框架
2、Algo的作用:数据库分库后,替代SQL功能 替代复杂SQL 替代临时表 报表框架
二、原理:![](https://i-blog.csdnimg.cn/blog_migrate/9369494abd5325687183701c5de501b7.png)
1、DataSet的定义:
DataSet是结果集:类似JDBC ResultSet 结果集转换器 迭代计算
接口类:kd.bos.algo.DataSet
两类接口: Transform,转换DataSet,生成新的DataSet Action,消费DataSet
2、DataSet转换方法:
①、将一个DataSet转换成另一个DataSet 转换接口,包括主要sql操作,如orderby,select,where,filter,group by,range,top,union等等 可执行sql语句,executeSql
示例:
Dataset dataset =null;
Dataset test =dataset.executesql( "select * from test" );
②、Action是消费DataSet,Action执行结果不是DataSet,Action执行后,DataSet自动关闭。
Action接口不多,比如count,cache
3、DataSet引用关系树
DataSet经过多次转换后,会形成一个引用关系树,如:
DataSet c = a.union(b);
DataSet e = c.select(new String[]{“fid”,”fname”});
DataSet f = c.join(d,xxx).finish(); 非叶子节点都是被引用的DataSet 每个DataSet节点都会消耗资源或者引用资源,close时会释放资源 DataSet.close时,会自动close引用节点 所以,所有叶子节点close,会自动close整棵树 叶子节点必须close
三、Algo API对象
接口/对象类 | 作用 | 说明/注意 |
kd.bos.algo.Algo | 创建DataSet getCacheDataSet(String cacheId) | 这个类是入口,给平台使用的,业务不要使用 |
kd.bos.algo.DataSet | DataSet操作接口,包括转换,action,close等 | |
Kd.bos.algo.RowMeta | DataSet的元数据,类似jdbc ResultSetMetaData 由字段Field[]组成 | |
Kd.bos.algo.Field | 字段 | |
Kd.bos.algo.DataType | 数据类型,包括主要数据类型 | |
Kd.bos.algo.Input | 数据源接口 | |
Kd.bos.algo.Row | 行接口,行值访问器 | Row是一个虚接口,访问器,不是Object[],所以业务代码不能把Row缓存起来 |
CachedDataSet | DataSet.cache的结果对象 | 不是DataSet的子类 |
CacheHint | Cache hint,指示 | |
JoinDataSet | DataSet.join的结果对象,也是Join构造器 | 不是DataSet的子类 |
HashJoinDataSet | DataSet.hashJoin的结果对象,也是HashJoin构造器 | 不是DataSet的子类 |
GroupbyDataSet | DataSet.groupBy的结果对象,也是groupBy构造器 | 不是DataSet的子类 |
HashTable | DataSet.toHashTable的结果对象,是hashJoin的参数 | |
JoinType | Join类型枚举 | |
JoinHint | Join hint,指示 | |
RowMetaFactory | 创建RowMeta | 给平台用的 |
1、获取元数据RowMeta,Field,DataType
DataType类型 行遍历器 ,Row接口 DataSet接口 Transform Action Close
RowMeta rowMeta = dataSet.getRowMeta();
Field field = rowMeta.getField(index);
2、行遍历器:
类似ResultSet,提供遍历方法 得到遍历器:Iterator<Row> iterator();
语法糖:for(Row row:DataSet)遍历,隐含调用了iterator方法 每个DataSet只能显式得到一次遍历器 虽然被引用的DataSet内部可能会被遍历多次
3、Row接口:
public int size(); | public Boolean getBoolean(int index); |
public Object get(int index); | public Boolean getBoolean(String field); |
public Object get(String field); | public Double getDouble(int index); |
public String getString(int index); | public Double getDouble(String field); |
public String getString(String field); | public BigDecimal getBigDecimal(int index); |
public Integer getInteger(int index); | public BigDecimal getBigDecimal(String field); |
public Integer getInteger(String field); | public Date getDate(int index); |
public Long getLong(int index); | public Date getDate(String field); |
public Long getLong(String field); | public Timestamp getTimestamp(int index); |
public Timestamp getTimestamp(String field); |
4、DataType类型:
DataType type = field.getDataType();
BooleanType | |
StringType | |
IntegerType | |
LongType | |
DoubleType | 从元数据ORM框架查出来的数据不会有Double |
BigDecimalType | |
DateType | |
TimestampType | |
NullType | Select中动态创建null Field生成 |
UnknownType | 表达式解析后无法确定的列,业务不会用到 |
5、创建DataSet
①、业务使用: QueryServiceHelper. queryDataSet(String algoKey, String entityName, String selectFields, QFilter[] filters,String orderBys) ORM.queryDataSet(String algoKey, String entityName, String selectFields, QFilter[] filters,String orderBys) DB.queryDataSet();
②、Algo原生: Algo algo = Algo.create(algoKey); public abstract DataSet createDataSet(Collection<Object[]> rowList, RowMeta rowMeta); public abstract DataSet createDataSet(Iterator<Object[]> iterator, RowMeta rowMeta); public abstract DataSet createDataSet(ResultSet rs); public abstract DataSet createDataSet(Input… inputs);
③、通过Input创建DataSet,请参考高级API
6、什么是algoKey
无论由哪种方法创建DataSet,都必须传algoKey algoKey是用来: 监控追踪DataSet的使用情况,包括日志,性能(IO,CPU,时间),执行计划 可根据algoKey动态配置DataSet执行行为 比如:algoKey=fi.abc,未来可能的配置项包括: algo.fi.abc.env=local/cluster/spark algo.fi.abc.maxrowcount=100000 algo.fi.abc.querytimeout=60*1000 algoKey定义规范: fi.gl.AccountBalance 调用类名字Class.getName()
四、DataSet 接口-sql查询
除了需要两个DataSet的操作,比如Join,Union,大部分都支持SQL查询 基于SQL查询得到DataSet和基于API得到DataSet是等价的
方法 | 功能 |
DataSet executeSql(String sql) | 执行sql语句 |
DataSet接口,表达式语法在文档说明
方法 | 功能 |
select(String[] exprs) select(String exprs) | 选择字段,类似: select a,b,c,a,b,c可以是表达式 |
addField(String expr, String alias) | 增加字段 |
addNullField(String alias) | 增加null字段,等价于addField(“null”,alias) |
addBalanceField(String expr, String alias) | 增加余额字段,该字段的值从上往下累加 |
removeFields(String... fields) | 删除字段 |
filter(String expr) filter(String expr, Map<String, Object> params) where(String expr) where(String expr, Map<String, Object> params) | 过滤,类似where,expr中可以写变量,变量值通过params传递,比如”a>b”, params是map,key包含a和b |
orderBy(String[] fields) | 排序,field可以写成field asc,field desc |
groupBy(String[] groupFields) groupBy(String[] groupFields, boolean[] orderByDescs) | groupBy操作,后续详细说明 |
DataSet join(DataSet rightDataSet) DataSet join(DataSet rightDataSet, JoinHint hint) DataSet join(DataSet rightDataSet, JoinType type) DataSet join(DataSet rightDataSet, JoinType type, JoinHint hint) DataSet leftJoin(DataSet rightDataSet, JoinHint hint) DataSet rightJoin(DataSet rightDataSet, JoinHint hint) DataSet fullJoin(DataSet rightDataSet, JoinHint hint) | Join操作,后续详细说明 |
DataSet hashJoin(HashTable hashTable, String leftJoinKeyField, String[] DataSet hashTableSelectFields) DataSet hashJoin(HashTable hashTable, String leftJoinKeyField, String[] DataSet hashTableSelectFields, boolean includeNotExist) | HashJoin操作,后续详细说明 |
union(DataSet dataSet) | Union操作 |
union(DataSet… dataSets) | Union操作,同时Union多个 |
top(int length) | 取top n个 |
limit(int start, int length) range(int start, int length) | 取部分行,类似limit start,length |
copy() | 拷贝自己,后面详细说明 |
int count(String field, boolean distinct) | 对field进行count计数,distinct消除重复 |
CachedDataSet cache(CacheHint cacheHint) CachedDataSet.Builder cacheBuilder(CacheHint hint) | 将dataset缓存起来,后续详细介绍 |
print(boolean copy) | 打印输出到console,copy表示是否copy dataset。调试时,可以调用print(true)打印 |
Close() | 关闭 |
isEmpty() | 判断是否为空 |
Iterator<Row> Iterator() | 返回Iterator |
boolean hasNext() Row next() | DataSet本身也是个Iterator,可以直接遍历 |
1、API:executeSql
①、方法:
public DataSet executeSql(String sql);
②、Sql语法:
SELECT select_list [ WHERE search_condition ] [ GROUP BY group_by_expression ] [ HAVING search_condition ] [ ORDER BY order_expression [ ASC | DESC ] ] [LIMIT [start,]length]
没有from table 目前还不支持join,hashjoin,union,请使用api方式 除了join,hashjoin,union,其他api都可以用sql查询替代 group by和order by只支持字段,还不支持表达式和函数 Sql函数不全,支持的表达式和函数请看后面文档,具体看文档
示例:
DataSet dataSet2 = dataSet.executeSql("select count(*) c,fname,1 one, null N,'abc' Str, 1+2 as Add,sum(famount) amount, sum(famount*2) amount2,sum(fqty) qty where famount>10 group by fname having amount>1000 order by amount desc,qty limit 0,10");
2、API:Select
public DataSet select(String[] exprs);
Expr可以是字段field Field as alias 表达式 as alias 表达式,语法参考后面
Select是对源DataSet的重新选择,类似sql: DataSetA.Select(new String[]{“a”,”b+c as d”}) 等价于: Select a,b+c as d from DataSetA
public DataSet select(String exprs) exprs可以是直接写多个字段,比如dataSetA.select(“fname, famount as amount,1+2 as c”);
示例:
DataSet dataSet3 = dataSet.select(new String[]{"famount","famount c1", "famount + fqty c2", "famount * fqty c3", "null c4", "true c5", "(famount + fqty) * 3 c6", "famount >= fqty c7", "famount >= fqty and fqty<100 c8", "fname + 'abc' name"});
或者: DataSet dataSet3 = dataSet.select("famount, famount c1, famount + fqty c2, famount * fqty c3, null c4, true c5, (famount + fqty) * 3 c6, famount >= fqty c7, famount >= fqty and fqty<100 c8, fname + 'abc' name});
3、API:add*Field/removeField
在现有DataSet基础上添加字段,或者删除字段 addField(String expr, String alias),添加表达式expr字段,数据类型自动判断 addNullField, 添加值为Null的字段,数据类型为NullType addBalanceField(String expr, String alias),添加余额字段 removeField(String field),删除字段
余额字段是什么? 举例:addBalanceField(“amount”, “balance”), 举例:addBalanceField(“amount * 2”, “balance2”), 结果如下:
4、API :where or filter
where和filter完全等价 public DataSet where(String expr); public DataSet where(String expr, Map<String, Object> params); public DataSet filter(String expr); public DataSet filter(String expr, Map<String, Object> params); Filter类似于sql的where条件 Expr也是计算表达式,必须为boolean Params传递参数给expr,如 expr为famount + var1 > 10000 Params为:{var1: 100} 等价于: famount+100>10000
示例:Where/Filter例子
DataSet dataSet2 = dataSet.where("famount>50 and (fqty-100)<5"); DataSet dataSet2 = dataSet.filter("famount>50 and (fqty-100)<5");
5、API :orderBy
public DataSet orderBy(String[] exprs); Expr可以是: 字段Fname,默认为asc排序 Fname asc Fname desc Expr不可以为表达式
示例:
DataSet dataSet2 = dataSet.orderBy(new String[]{"fname", "famount"}); DataSet dataSet2 = dataSet.orderBy(new String[]{"fname desc", "famount asc"});
6、API:groupBy
GroupbyDataSet groupBy(String[] groupFields); GroupbyDataSet groupBy(String[] groupFields, boolean[] orderByDescs); 调用过程: 获取构造器GroupByDataSet 构造器里设置sum,avg等聚合函数 构造器finish得到DataSet
注: 如果觉得Group by api写法比较麻烦,建议使用sql查询
GroupByDataSet构造器方法
聚合函数返回GroupByDataSet,finish方法返回DataSet
类型 | 方法 | 说明 | 其他 |
普通聚合函数 | sum(String field) sum(String expr, String alias) | expr:表达式 | |
avg(String field) avg(String expr, String alias) | expr:表达式 | ||
max(String field) max(String expr, String alias) | expr:表达式 | ||
min(String field) min(String expr, String alias) | expr:表达式 | ||
count() count(String alias) | 等价于select count(1) 等价于select(1) | count不需要指定field,因为count(1)=count(*) | |
特殊聚合函数 | maxP(String field, String propertyField) maxP(String expr, String propertyField,String alias) | 用field/expr计算最大行,结果取propertyField字段 | |
minP(String field, String propertyField) minP(String expr, String propertyField, String alias) | 用field/expr计算最小行,结果取propertyField字段 | ||
自定义函数 | agg(CustomAggFunction<> func, String expr, String alias); | 参考高级API | |
控制 | Finish | 返回DataSet接口 |
示例:
GroupbyDataSet groupby = dataSet.groupBy(new String[]{“fname”}); //groupby是构造器 groupby = groupby.sum("(famount + fqty) * 2", "amount").count(“countalias"); groupby = groupby.maxP(“famount”, “fqty”, "amount").count(); DataSet dataSet2 = groupby.finish();//构造完毕,返回DataSet
注: 如果觉得Group by api写法比较麻烦,建议使用sql查询
关于GroupBy结果的排序
GroupBy结果集默认是根据group fields排序,默认分别是asc排序,可以对每个field的排序方向进行设置,即以下orderByDescs: GroupbyDataSet groupBy(String[] groupFields, boolean[] orderByDescs); 所以,对groupby结果进行orderBy,请考虑是否有必要
注: sql 92标准,group by结果是不保证排序的,上述API查询结果默认是排序的,是历史原因,目前无法简单去掉 现在没有sql的group by,结果就不保证排序
GroupBy的退化:
当GroupBy字段为空时,如 dataset.groupBy(null),或者 dataSet.groupBy(new String[0]) dataSet.groupBy(null).sum(a).max(b).finish()等价于没有groupby但有聚合函数的sql: Select sum(a),max(b) from t
注: 如果用sql,就可以用sql的简单写法: Select sum(a),max(b), 不需要group by关键字
7、API:union
public DataSet union(DataSet rightDataSet); Public DataSet union(DataSet... dataSets); 两个DataSet叠加起来,不支持消除重复 两个DataSet字段个数必须相同,且每个字段的类型必须互相兼容 字段Fields合并规则: 默认字段名取左边 数据类型取左边和右边的兼容类型 数据类型兼容规则: Number类型,取最大精度如(Int,Long)取long,(BigDecimal,Long)取BigDecimal 含Null类型的,取非Null类型 含Unknown类型的,取非Unknown类型的 其他 如果两个DataSet字段个数不同,又需要Union,可以通过DataSet.addNullField()添加null字段,以满足相同个数
注: 这种代码A.union(B).union(C)….union(N)应该优化为: A.union(B, C, …, N), 提升性能
8、API: HashJoin/Join
Algo的Join方法从大类上分为两种: 普通Join:适用于两个DataSet的join,跟sql的join类似,支持InnerJoin,LeftJoin,RightJoin,FullJoin,CrossJoin 星型hashJoin:适用于一个大表关联1至N个小表,如业务单据join基础资料 hashJoin比普通Join性能更好,特别是同时关联多个小表时 HashJoin性能比较好,消耗资源少,推荐使用,但也谨慎推荐,因为小表是否真的很小,需要判断,一般认为小表数据量小于10000即可
满足星型结构:
使用方法:
1. 小表DataSet转换为HashTable public HashTable toHashTable(String keyField); keyField必须是唯一单字段,否则就不能是HashTable
2. 大表DataSet.hashJoin小表,得到HashJoinDataSet构造器 public HashJoinDataSet hashJoin(HashTable hashTable, String leftJoinKeyField/*大表关联字段*/, String[] hashTableSelectFields/*右边选择字段*/); HashJoinDataSet构造器addHashTable,继续添加 调用过程: 如果要关联N个小DataSet,分别转换为HashTable 通过hashJoin获得构造器HashJoinDataSet 在构造器中添加HashTable,设置左表选择字段 Finish()返回DataSet
HashJoinDataSet构造器方法:
添加一个HashTable关联: HashJoinDataSet addHashTable(HashTable hashTable, String leftJoinKeyField, String[] hashTableSelectFields, boolean includeNotExist); 设置左边的选择字段HashJoinDataSet selectLeftFields(String[] leftFields); finish()
includeNotExist选项
HashJoin默认结果类似于InnerJoin 也可以实现Left Join的效果,即大表中有,小表中没有,通过includeNotExist 大表连接HashTable时,如left.faccountid=hashTable.fid, 如果left.faccountid在HashTable中不存在,默认是不会选择该行 如果传入includeNotExist=true,那么会选择该行,等价于left join,右表字段全部为null includeNotExist是作用于每个连接上,所以,每个连接可以includeNotExist值不同的
HashJoin 示例:
HashTable productLT = productDataSet.select(new String[]{"fid productId", "fname productName"}).toHashTable("productId");
HashTable orgLT = orgDataSet.select(new String[]{"fid orgId", "fname orgName"}).toHashTable("orgId");
DataSet joinDataSet = factDataSet.hashJoin(productLT, "fproductid", new String[]{"productName as pName"}, true) .addHashTable(orgLT, "forgid", new String[]{"orgName"}, true) .selectLeftFields(new String[]{"fproductid", "famount amount", "fqty qty"}).finish();