DQL(Dimentinal Query Language)语言是润乾公司提出的一种面向OLAP的多维数据查询语言,可以将复杂的主子表很简单的整合为一个大宽表来查询。具体介绍可以参考乾学院的文章
告别宽表,用 DQL 成就新一代 BI - 乾学院
另外可以参见dql.md
在Nop平台中,NopORM也提供了类似的查询方式,可以通过QueryBean
来查询多个表的数据,而不需要关心表之间的关联关系。
QueryBean表达多表查询 比如对于下面的表结构
NopAuthGroup <--- NopAuthGroupDept ---> NopAuthDept
如果要查询NopAuthGroup
表并同时返回每个分组关联的部门的总数,类似于下面的SQL语句
select o.groupId, o.name, (select count (g.deptId) from NopAuthGroupDept g where g.groupId= o.groupId) as deptCountfrom NopAuthGroup o
可以通过QueryBean
来实现
QueryBean query = new QueryBean (); query.setSourceName (NopAuthGroup .class .getName ()); query.fields (mainField ("groupId" ), mainField ("name" ), subField ("deptMappings" , "deptId" ).count ().alias ("deptCount" )); query.addOrderField ("name" , true );List <Map <String , Object >> list = ormTemplate.findListByQuery (query);
上面的代码中,mainField
表示主表字段,subField
表示子表字段,subField的第一个参数是关联的字段名,第二个参数是要查询的子表上的字段名,count
表示统计子表的总数,alias
表示别名。
实际的实现原理不是生成一个复杂SQL,而是在内存中分成多个查询,然后在内存中通过HashJoin来把数据整合为一个大宽表。
select o.groupId, o.name from NopAuthGroup o;select g.groupId, count (g.deptId) as deptCount from NopAuthGroupDept g group by g.groupId;
这样的查询方式可以大大简化复杂的多表查询,提高开发效率。
分页和复合关联属性 QueryBean
支持设置offset和limit属性,从而基于主表进行分页查询。此外,因为底层的运行引擎是NopORM,而不是普通的JDBC查询引擎,因此它会自动识别复合属性,并自动展开为表关联关系。
QueryBean query = new QueryBean (); query.fields (mainField ("refField.name" ), subField ("deptMappings" ,"otherRefField.user.name" ).count ().alias ("count" )); query.addFilter (FilterBeans .eq ("refField.status" ,1 )); query.setOffset (100 ); query.setLimit (10 );
这里假定主实体上存在名为refField
的to-one
关联,而deptMappings
是一个to-many
关联,otherRefField
是deptMappings
关联的另一个to-one
关联。
增加复杂子查询 select t1.course_name as c1 , count (o.student_id) as c2from course_selection as o join course as t1 on o.course_id = t1.course_idwhere o.student_id in ( select t.student_id as c3 from student_follow as t where t.follower_id = 1 )group by t1.course_name
QueryBean中可以通过sql子节点来追加复杂过滤条件
QueryBean query = new QueryBean (); query.addField (QueryFieldBean .forField ("course.courseName" )); query.addField (QueryFieldBean .forField ("studentId" ).aggFunc ("count" ).alias ("cnt" ));SQL sql = SQL .begin ().sql ("o.studentId in (select t.studentId from StudentFollow t where followerId = 1)" ).end (); query.addFilter (FilterBeans .sql (sql)); query.setSourceName ("CourseSelection" );orm ().findListByQuery (query);
如果是sql-lib
中的query节点,
<query name ="testQueryBean" sqlMethod ="findAll" > <source > <sourceName > CourseSelection</sourceName > <fields > <field name ="course.courseName" /> <field name ="studentId" aggFunc ="count" alias ="cnt" /> </fields > <filter > <filter:sql xpl:lib ="/nop/core/xlib/filter.xlib" > o.studentId in (select t.studentId from StudentFollow t where followerId = 1) </filter:sql > </filter > <groupBy > <field name ="course.courseName" /> </groupBy > </source > </query >
在类似MyBatis的sql-lib中管理动态构建的QueryBean <sql-lib > <sqls > <query name ="queryGroupWithDeptCount" sqlMethod ="findList" > <source > <sourceName > NopAuthUser</sourceName > <fields > <field name ="groupId" /> <field name ="name" /> <field owner ="deptMappings" name ="deptId" aggFunc ="count" alias ="deptCount" /> </fields > <filter > <c:if test ="${someCondition}" > <eq name ="status" value ="${status}" /> </c:if > </filter > <orderBy > <field name ="name" asc ="true" /> </orderBy > </source > </query > </sqls > </sql-lib >
<query>
的source段就是一段xpl模板语言,它生成QueryBean的一个XML表示。在生成过程中,可以使用Xpl标签来实现进一步的抽象。
通过sqlMethod
我们可以选择使用findList
、findFirst
或者exists
等方法,分别用于返回列表数据、第一条数据或者判断是否存在数据。
通过Underscore帮助类对数据进行加工 Underscore.java
工具类提供了一些针对集合对象的帮助函数,比如leftjoinMerge
可以实现两个列表的join合并。
Underscore .leftjoinMerge (listA,listB, leftPropName, rightPropName, Arrays .asList (fldB1,fldB2));
以上函数调用相当于实现
select listA.* , listB.fldB1,listB.fldB2from listA, listBwhere listA.leftProp = listB.rightProp
集成tablesaw nop-tablesaw
模块集成了tablesaw
计算包,它的功能类似于python中的pandas库,可以完成一系列针对列表数据的统计计算。
Table table = DataSetHelper .dataSetToTable (dsName, dataSet); table.numberColumn ("count" ).sum ();
NopORM查询得到的IDataSet
数据集可以直接被转换为tablesaw
的Table
接口,然后就可以调用select/pivot/summarize/count
等一系列操作函数。
NopORM内部将所有的数据集合对象都统一封装为IDataSet接口,因此并不对外暴露ResultSet等泄露实现细节的接口。NopORM底层可以不运行在JDBC之上。