sybn sybn-util 项目说明文档 - 基于java的跨数据库联合查询

sybn dao 使用子查询与union

2018-12-20
sybn

简介

标准的 sql 语句中查询嵌套查询和很常见的,但是 sybn dao 默认都只支持单表查询。

为了解决查询嵌套的问题, 准备了专门的 dao 实现: SqlDdlDaoMultipleImpl

-- 本层查询由 java 执行
select name, sum(c) as c from (

  -- 启动时会通过 show tables 注册每个数据库有那些表 

  -- 本层查询路由到 mysql, 依据来自 from 和 join 的表名
  select name, count(*) as c from sql_demo_table group by name
  
  -- union 操作由 java 执行
  union all 
  
  -- 本层查询路由到 elastic search, 如果表重名则加数据库名即可
  select name, count(*) as c from es.demo_table group by name
  union all
  
  -- 本层查询路由到 mongo, sql 会被转为 aggergate 表达式, mongo 1 != "1"
  select name, count(*) as c from mongo.demo_table group by name
  union all
  
  -- 本层查询路由到 presto, sql 会被转为 presto 方言格式, 支持 ${hiveconfig:x}
  select `name`, count(*) as c from presto_demo_table group by name
  union all
  
  -- 本层查询路由到 hbase, 注意 group by 部分由 java 实现, 所有比较操作为字符串比较
  select name, count(*) as c from hbase_demo_table group by name
  
) group by name

在线测试

可以用以下链接尝测试执行sql, 其中的 sql 及 json数据 可以随意替换. 也可以使用测试表: sql_demo_table,mongo_demo_table,cinema_info

使用样例

  • 准备 SqlDdlDaoMultipleImpl
// 初始化各种数据源, 为了容易理解这里使用直接传入jdbc的实现类
SqlDdlDao dao1 = new DbutilDaoImpl("jdbc:mysql://账户:密码@192.168.4.31:3306,192.168.4.32:3306/test"); // sql
SqlDdlDao dao2 = new MongoDaoImpl("mongodb://账户:密码@192.168.4.31:27017,192.168.4.32:27017/test"); // mongo
SqlDdlDao dao3 = new SolrDaoImpl("solr://192.168.7.71:2181,192.168.7.72:2181/solr"); // solr
SqlDdlDao dao4 = new EsDaoImpl("es://192.168.7.71:9200,192.168.7.72:9200"); // es
SqlDdlDao dao5 = new HBaseDaoImpl("hbase://192.168.7.71,192.168.7.72/hbase-unsecure"); // HBase
// 推荐使用配置文件管理数据库连接信息, 比如: SqlDdlDao dao1 = new DbutilDaoConfImpl("mysql_test@xxx.properties")

// 构造 SqlDdlDaoMultipleImpl, 并将以上数据源注册进来.
SqlDdlDaoMultipleImpl multipleDao = new SqlDdlDaoMultipleImpl();
multipleDao.addAllTableSource(dao1);
multipleDao.addAllTableSource(dao2);
multipleDao.addAllTableSource(dao3);
multipleDao.addAllTableSource(dao4);
multipleDao.addAllTableSource(dao5);
  • from 子查询
String sql = "
	select type_name,sum(count) as count from (
		select type,count(*) as count from table1 group by type;
		select type,type_name from table2;
		join right(type_name) on left.type = right.type;
	) group by type_name";
List<Map<String,Object>> mapList = multipleDao.sqlFindListMap(sql);
List<Bean> beanList = multipleDao.sqlFindList(sql, Bean.class);
  • select 子查询 V:0.2.19
String sql = "
	select * from table1 where id in (
		select id from table2
	);"
List<Map<String,Object>> mapList = multipleDao.sqlFindListMap(sql);
List<Bean> beanList = multipleDao.sqlFindList(sql, Bean.class);
  • union V:0.2.18
String sql = "
	select * from table1 
	union all
	select * from table2;"
List<Map<String,Object>> mapList = multipleDao.sqlFindListMap(sql);
List<Bean> beanList = multipleDao.sqlFindList(sql, Bean.class);
  • 临时变量 V:0.3.4
-- 支持 mysql风格的临时变量
set @time_date_str := '2019-01-16',
    @time := str_to_date(@time_date_str, '%Y-%m-%d');
select * from table where time_str > @time_date_str and time > @time

-- 扩展支持 list 型变量
set @a@list = (1028, 1029, 1030, 1031, 1032);
select * from table where id in (@a@list);

-- 扩展支持 list 型变量,并内嵌函数和变量
set @a = 1032;
set @a@list = (1028, CONVERT("1029", SIGNED), CAST("1030" as SIGNED), toInt("1031"), @a);
select * from table where id in (@a@list);

-- 扩展支持 list 型变量,并从查询获取值
set @a@list = (SELECT id FROM table order by id limit 5);
select * from table where id in (@a@list);

不支持功能

因为 join 的功能还没写完所以暂时使用 select + select + join 的方式实现 join 操作。

-- ## 第一次 join

-- table1 的 sql 的返回值会被压入堆栈,坐标[0]
select type,count(*) as count from table1 group by type;
-- table2 的 sql 的返回值会被压入堆栈,坐标[1]
select type,type_name from table2;
-- join 会按先进后出的原则从堆栈的结尾获取表, 先取出[1]定义为 right, 再取出[0]定义为 left
-- join 会将 right 的 type_name 写入 left,然后从堆栈删除 right [1], 此时堆栈只剩下[0]
join right(type_name) on left.type = right.type;

-- ## 再次 join (按顺利连续join)

-- table3 的 sql 的返回值会被压入堆栈,坐标[1]
select type_name_creatime,type_name from table3; -- # 第二次的 join 会将其前方此sql的返回值认定为 right 表, 而其前方第二张表也就是 第一次精品
-- join 会按先进后出的原则从堆栈的结尾获取表, 先取出[1]定义为 right, 再取出[0]定义为 left
-- join 会将 right 的 type_name_creatime 写入 left,然后从堆栈删除 right [1], 此时堆栈只剩下[0]
join right(type_name_creatime) on left.type_name = right.type_name; -- # join 会将 right 表指定字段查询 left 表,然后返回 left 表

-- ## 嵌套 join 右表本身有 join 逻辑

-- table1 的 sql 的返回值会被压入堆栈,坐标[1]
select type,type_id from table4;
-- table2 的 sql 的返回值会被压入堆栈,坐标[2]
select type_id, type_id_createtime from table4;
-- join 会按先进后出的原则从堆栈的结尾获取表, 先取出[2]定义为 right, 再取出[1]定义为 left
-- join 会将 right 的 type_name 写入 left,然后从堆栈删除 right [2], 此时堆栈只剩下[0]和[1]
join right(type_id_createtime) on left.type = right.type;

注意事项

  • 关于返回值列顺序

SqlDdlDaoMultipleImpl 返回 map 时,只保证第一条数据的返回字段顺序与 select 语句一致, 比如:

select a,b,c from mongo_table
-- 返回值可能如下形式:
-- [{a:1,b:2,c:3}, // 第一条,字段顺序与sql一致
-- {b:2,a:1,c:3}, ...] // 第二条及之后内容,字段顺序可能是hash顺序

考虑到列排序对于性能的影响,某些情况下(比如数据量较少时)可能会对所有数据排序,但返回值行数较多时只保证第一行的顺序。

如果须要严格将所有数据的每一列顺序对齐,可以要求返回 java bean,或者使用以下工具类排序:

MapsUtil.orderMaps(List<Map<String, V>> source, List<String> keys);
MapsUtil.orderMaps(Stream<Map<String, V>> source, List<String> keys);

相关页面


Similar Posts

下一篇 数据查询

Comments

暂不开放评论! 可微信联系