本节重点总结olap4j开发
根据上一章节的schemal,我们用olap4j方式实现查询。
package com.muge.as.dao; import mondrian.olap.Util; import org.olap4j.Axis; import org.olap4j.CellSet; import org.olap4j.OlapConnection; import org.olap4j.layout.CellSetFormatter; import org.olap4j.layout.RectangularCellSetFormatter; import org.olap4j.mdx.IdentifierNode; import org.olap4j.metadata.Cube; import org.olap4j.metadata.Member; import org.olap4j.metadata.NamedList; import org.olap4j.query.Query; import org.olap4j.query.QueryDimension; import org.olap4j.query.Selection; import java.io.InputStream; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; //import mondrian.olap.Connection; /** * @author xj */ public class TestFSCK_olap4j_m4 { private static String driver = "oracle.jdbc.driver.OracleDriver"; private static String url="jdbc:oracle:thin:@127.0.0.1:1521:ORCL"; private static String userName = "superVision"; private static String password = "1"; // 立方体定义文件 //private static String xmlFile = "sample.xml"; //private static String xmlFile = "./foodmart_fd/olapSchema2.xml"; private static String xmlFile = "FSCK.xml"; public static void main(String[] args) throws Exception { // olap4j的mdx接口,是一个jdbc实现,推荐 System.out.println("\r\n===================olap4j接口=================="); //String mdxStr = "select {[Year].Members} on columns,{[Entity].Members} on rows from 模型一"; // 建立连接 String strUrl = "jdbc:mondrian:"; strUrl += "Jdbc=" + url; strUrl += ";JdbcUser=" + userName; strUrl += ";JdbcPassword=" + password; // strUrl += ";Catalog=" + xmlFile; strUrl += ";CatalogContent=" + getCatalogContent(); Class.forName("mondrian.olap4j.MondrianOlap4jDriver"); Connection olap4jConn = DriverManager.getConnection(strUrl); OlapConnection olapConn = (OlapConnection) olap4jConn.unwrap(OlapConnection.class); NamedList<Cube> cubes = olapConn.getOlapSchema().getCubes(); Cube cube = cubes.get("Col_trade_detail"); Query myQuery = new Query("model1Query", cube); myQuery.execute(); System.out.println("------------myQuery------------:"+myQuery); QueryDimension year = myQuery.getDimension("Year"); QueryDimension account = myQuery.getDimension("Account"); myQuery.getAxis(Axis.COLUMNS).addDimension(year); myQuery.getAxis(Axis.ROWS).addDimension(account); Member year2010 = cube.lookupMember( IdentifierNode.ofNames ("Year", "2010年") .getSegmentList()); year.include(year2010); account.include( Selection.Operator.CHILDREN, IdentifierNode.ofNames("Account", "其他收入", "其他收入") .getSegmentList()); myQuery.validate(); System.out.println( myQuery.getSelect().toString()); String s=myQuery.getSelect().toString(); System.out.println("s:"+s); CellSet cellSet = myQuery.execute(); // 输出结果 CellSetFormatter formatter = new RectangularCellSetFormatter(false); formatter.format(cellSet, new PrintWriter(System.out, true)); } /** * 初始化数据库,建表 * * @throws Exception */ private static void initDB() throws Exception {} private static String getCatalogContent() throws Exception { InputStream inputStream =null; try { inputStream= Util.readVirtualFile(xmlFile); final byte[] bytes = Util.readFully(inputStream, 1024); String s=new String(bytes, "UTF-8"); return s; }catch(Exception e){ e.printStackTrace(); return ""; } finally { if (inputStream != null) { inputStream.close(); } } } }结果如下2段内容:
s:SELECT {[Year].[YearLy].[2010年]} ON COLUMNS, {[Account].[Account_L].[其他收入].[其他收入].Children} ON ROWS FROM [Col_trade_detail]生成的规则是:维度-->Hierarchy name='Account_L'---><Level attribute="Level1projectname"/>的元素-->2的元素
| | 2010年 | +------+------+----------------+------------+ | 其他收入 | 其他收入 | #null | | | | | 办公楼处置 | | | | | 产权交易佣金收入 | | | | | 场租收入 | | | | | 城市基础设施配套手续费 | | | | | 出租广告位 | | | | | 档案整理综合服务费 | | | | | 房改办证工本费 | | | | | 房租收入 | **.66 | | | | 附属单位缴款 | | | | | 工程安全生产保证金 | 69**00. | | | | 公安保证金 | 5*0. | | | | 挂号收入 | |
看到这个数字,我想探索一下非税收入中历年(09年-17年)房租变化,动手修改语句:
year.include(year2009); year.include(year2010); year.include(year2011); year.include(year2012); year.include(year2013); year.include(year2014); year.include(year2015); year.include(year2016); year.include(year2017); account.include( Selection.Operator.CHILDREN, IdentifierNode.ofNames("Account", "其他收入", "其他收入","房租收入") .getSegmentList());自动生成如下mdx:
SELECT {[Year].[YearLy].[2009年], [Year].[YearLy].[2010年], [Year].[YearLy].[2011年], [Year].[YearLy].[2012年], [Year].[YearLy].[2013年], [Year].[YearLy].[2014年], [Year].[YearLy].[2015年], [Year].[YearLy].[2016年], [Year].[YearLy].[2017年]} ON COLUMNS, {[Account].[Account_L].[其他收入].[其他收入].[房租收入].Children} ON ROWS FROM [Col_trade_detail]执行结果如下:
从结果看,该县9年间,政府房租收入涨了9倍多。
上面生成的mdx语句中:[Account].[Account_L].[其他收入].[其他收入].[房租收入].Children
因此 结果中有个#null,如何去掉这个,还是个问题。
有包括必然有排除某个维度成员的方法,如下例排除垃圾清运费:
account.exclude(IdentifierNode.ofNames("Account", "其他收入", "其他收入","垃圾清运费" ) .getSegmentList());生成的mdx如下:
SELECT {[Year].[YearLy].[2009年], [Year].[YearLy].[2010年], [Year].[YearLy].[2011年], [Year].[YearLy].[2012年], [Year].[YearLy].[2013年], [Year].[YearLy].[2014年], [Year].[YearLy].[2015年], [Year].[YearLy].[2016年], [Year].[YearLy].[2017年]} ON COLUMNS, {Except({[Account].[Account_L].[其他收入].[其他收入].Children}, {[Account].[Account_L].[其他收入].[其他收入].[垃圾清运费]})} ON ROWS代码中,年度添加的方式有点丑陋,后面再说。
遍历查询结果:关键词:Positions & Axis:
a cell is found by specifying a tuple of positions along every axis
ps:a tuple of 元组
沿着轴(axis)指定元组的位置,就可以找到对应的单元格。
上代码:
for (Position axis_0 : cellSet.getAxes().get(Axis.ROWS.axisOrdinal()).getPositions()) { for (Position axis_1 : cellSet.getAxes().get(Axis.COLUMNS.axisOrdinal()).getPositions()) { Cell currentCell = cellSet.getCell(axis_0, axis_1); Object value = currentCell.getValue(); } }关键是Position 对象及Axis对象。
后面,我打算自己开发展示层,会用到这里的位置及结果。
Sorting axis :
可以根据度量轴排序:
这个比较简单,官文中有例子,看一下就明白
(myQuery .getAxis(Axis.ROWS) .sort(SortOrder.DESC); )。
SortOrder.DESC、SortOrder.BDESC
这里就不展开。
olap4j对象:
解析与校验:
要保证mdx执行正确,最好先进行必要的解析与校验,parser用于分析语法,校验用于验证数据?
Parsing a query with the MdxParser:
1: MdxParserFactory pFactory = olapConnection.getParserFactory() 2: MdxParser parser = pFactory.createMdxParser(olapConnection); 3: try { 4: SelectNode parsedObject = parser.parseSelect("SELECT {} on Bacon FROM [MyCube]"); 5: } catch (OlapException e) { 6: System.out.println(e.getMessage()); 7: }MdxValidator 校验:
1: MdxParserFactory pFactory = olapConnection.getParserFactory(); 2: MdxParser parser = pFactory.createMdxParser(olapConnection); 3: SelectNode parsedObject = parser.parseSelect("SELECT {} on Bacon FROM [Sales]"); 4: MdxValidator validator = pFactory.createMdxValidator(olapConnection); 5: try { 6: validator.validateSelect(parsedObject); 7: } catch (OlapException e) { 8: System.out.println(e.getMessage()); 9: }纯mdx执行:
OlapConnection olapConn = (OlapConnection) olap4jConn.unwrap(OlapConnection.class); // 执行查询 OlapStatement statement = olapConn.createStatement(); CellSet cellSet = statement.executeOlapQuery(mdxStr);org.olap4j.mdx.SelectNode ---MDX Object model
可以用这个动态生成mdx:
MdxParserFactory pFactory = olapConn.getParserFactory(); MdxParser parser = pFactory.createMdxParser(olapConn); SelectNode selectNode = parser.parseSelect("SELECT {} ON ROWS FROM [Sales]"); selectNode .getAxisList() .get(Axis.COLUMNS.axisOrdinal()) .setExpression( IdentifierNode.parseIdentifier("[Product]")); System.out.println( selectNode.toString()); System.out.println("===============================================");控制台输出:
SELECT [Product] ON ROWS FROM [Sales] ===============================================IdentifierNode.parseIdentifier("[Product]"))放的是字符串,无法判断类型,为此,olap4j提供了CallNode对象。具体看附后的参考资料文档。
Scenarios, writeback and statistical simulations
olap4j提供了回写功能,这个回写会写入数据库吗,我没有研究,但mondrian4是支持回写的,我见过有公司通过回写编制全面预算的功能。我这里只是对财政非税数据进行分析,回写可以用于编制计划,用计划控制业务?
ps:参考资料:
Olap4j Introduction - An end-user perspective
上一章节 mondrian4 schemal
下一章节:mdx语法基础