OLAP分析(五)-olap4j

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/wh_xia_jun/article/details/90442648

本节重点总结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语法基础

猜你喜欢

转载自blog.csdn.net/wh_xia_jun/article/details/90442648