Mondrian MDX 執行原理剖析

Mondrian MDX 執行原理剖析

Mondrian是一個開源項目。一個用Java寫成的OLAP(在線分析性處理)引擎。它用MDX語言實現查詢,從關係數據庫(RDBMS)中讀取數據。然後經過Java API用多維的方式對結果進行展示。Mondrian如何將MDX轉換成相應的SQL去底層數據源查詢數據的?這個就是本文要剖析的問題。

模型結構

<code><schema><dimension>    <hierarchy>        <table>        <level>        <level>        <level>    /<hierarchy>/<dimension><dimension>    <hierarchy>        <table>        <level>        <level>        <level>    /<hierarchy>/<dimension><cube>        <table>            <dimensionusage>    <dimensionusage>            <measure>    <measure>    <measure>    <measure>            <calculatedmember>        <formula>[Measures].[銷售額] - [Measures].[成本]/<formula>        <calculatedmemberproperty>    /<calculatedmember>    /<cube>/<schema>/<code>

表結構

數據庫:derby

dim_time


Mondrian MDX 執行原理剖析

dim_store


Mondrian MDX 執行原理剖析

fact_sales


Mondrian MDX 執行原理剖析

查詢解析

1、單軸+單指標:查詢銷售額


Mondrian MDX 執行原理剖析

<code>select {[Measures].[銷售額]} ON COLUMNS from [銷售情況]/<code>

翻譯成的SQL:

<code>select sum("fact_sales"."sales") as "m0" from "fact_sales" as "fact_sales"/<code>

2、單軸+單維度和指標:查詢日期和銷售額


Mondrian MDX 執行原理剖析

<code> select {Crossjoin([時間].[年].Members, {[Measures].[銷售額]})} ON COLUMNS from [銷售情況]/<code>

一個軸上出現不同的維度需要使用 Crossjoin

翻譯成的SQL:

<code>-- 獲取所有的年份select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,         "dim_time"."time_year" ASC-- 求所有年份select count(distinct "time_year") from "dim_time"-- 獲取所有年份和求和數據select "dim_time"."time_year" as "c0",       sum("fact_sales"."sales") as "m0" from "dim_time" as "dim_time", "fact_sales" as "fact_sales" where "fact_sales"."time_id" = "dim_time"."time_id" group by "dim_time"."time_year"/<code>

3、單軸+雙維度和單指標:查詢日期和銷售額


Mondrian MDX 執行原理剖析

<code>select {  Crossjoin(                 Crossjoin([時間].[年].Members, [銷售點].[省].Members),                {[Measures].[銷售額]}    )      } ON COLUMNSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>// 獲取年select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,          "dim_time"."time_year" ASC          // 獲取省份 select "dim_store"."store_province" from "dim_store" as "dim_store"  group by "dim_store"."store_province"  order by CASE WHEN "dim_store"."store_province" IS NULL THEN 1 ELSE 0 END,          "dim_store"."store_province" ASC          select count(distinct "time_year") from "dim_time"  select count(distinct "store_province") from "dim_store"   select "dim_time"."time_year" as "c0", "dim_store"."store_province" as "c1",             sum("fact_sales"."sales") as "m0"   from "dim_time" as "dim_time", "fact_sales" as "fact_sales", "dim_store" as "dim_store"   where "fact_sales"."time_id" = "dim_time"."time_id"         and "fact_sales"."store_id" = "dim_store"."store_id"   group by "dim_time"."time_year", "dim_store"."store_province"  /<code>

4、雙軸+單維度和指標:查詢日期和銷售額

Mondrian MDX 執行原理剖析

<code>select {Crossjoin([時間].[年].Members, {[Measures].[銷售額]})} ON COLUMNS,  {[銷售點].[省].Members} ON ROWSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>// 獲取年select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,            "dim_time"."time_year" ASC  // 獲取省份 select "dim_store"."store_province" from "dim_store" as "dim_store"  group by "dim_store"."store_province"  order by CASE WHEN "dim_store"."store_province" IS NULL THEN 1 ELSE 0 END,         "dim_store"."store_province" ASC    // 獲取省份個數  select count(distinct "store_province") from "dim_store"    // 獲取日期個數  select count(distinct "time_year") from "dim_time"    // 獲取中間數據  select "dim_time"."time_year" as "c0", "dim_store"."store_province" as "c1",  sum("fact_sales"."sales") as "m0" from "dim_time" as "dim_time",   "fact_sales" as "fact_sales", "dim_store" as "dim_store"   where "fact_sales"."time_id" = "dim_time"."time_id"     and "fact_sales"."store_id" = "dim_store"."store_id"   group by "dim_time"."time_year", "dim_store"."store_province"         /<code>

5、雙軸+雙維度和指標:查詢日期和銷售額

Mondrian MDX 執行原理剖析

<code>select {Crossjoin([時間].[年].Members, {[Measures].[銷售額]})} ON COLUMNS,  {[銷售點].[市].Members} ON ROWSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_year" ASCselect "dim_store"."store_province", "dim_store"."store_city" from "dim_store" as "dim_store" group by "dim_store"."store_province", "dim_store"."store_city" order by CASE WHEN "dim_store"."store_province" IS NULL THEN 1 ELSE 0 END,       "dim_store"."store_province" ASC,       CASE WHEN "dim_store"."store_city" IS NULL THEN 1 ELSE 0 END,      "dim_store"."store_city" ASC       // 獲取城市個數 select count(distinct "store_city") from "dim_store"     // 獲取日期個數  select count(distinct "time_year") from "dim_time"      select "dim_time"."time_year" as "c0", "dim_store"."store_city" as "c1",     sum("fact_sales"."sales") as "m0"   from "dim_time" as "dim_time", "fact_sales" as "fact_sales",     "dim_store" as "dim_store"   where "fact_sales"."time_id" = "dim_time"."time_id"     and "fact_sales"."store_id" = "dim_store"."store_id"    group by "dim_time"."time_year", "dim_store"."store_city" /<code>

6、分頁雙軸+雙維度和指標:查詢日期和銷售額


Mondrian MDX 執行原理剖析

分頁的效果可以對比查看上圖。

<code>select {Crossjoin([時間].[年].Members, {[Measures].[銷售額]})} ON COLUMNS,  {Subset([銷售點].[市].Members, 0, 3)} ON ROWSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_year" ASCselect "dim_store"."store_province", "dim_store"."store_city" from "dim_store" as "dim_store" group by "dim_store"."store_province", "dim_store"."store_city" order by CASE WHEN "dim_store"."store_province" IS NULL THEN 1 ELSE 0 END,       "dim_store"."store_province" ASC,       CASE WHEN "dim_store"."store_city" IS NULL THEN 1 ELSE 0 END,      "dim_store"."store_city" ASC       // 獲取城市個數 select count(distinct "store_city") from "dim_store"     // 獲取日期個數  select count(distinct "time_year") from "dim_time"      select "dim_time"."time_year" as "c0", "dim_store"."store_city" as "c1",     sum("fact_sales"."sales") as "m0"   from "dim_time" as "dim_time", "fact_sales" as "fact_sales",     "dim_store" as "dim_store"   where "fact_sales"."time_id" = "dim_time"."time_id"     and "fact_sales"."store_id" = "dim_store"."store_id"    group by "dim_time"."time_year", "dim_store"."store_city" /<code>

從上面的SQL可以看出分頁其實是一個內存的分頁。

7、單字段排序

Mondrian MDX 執行原理剖析

<code>select {Crossjoin([時間].[年].Members, {[Measures].[銷售額]})} ON COLUMNS,  {Order([銷售點].[省].Members, [Measures].[銷售額], DESC)} ON ROWSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_year" ASC  select "dim_store"."store_province" from "dim_store" as "dim_store" group by "dim_store"."store_province" order by CASE WHEN "dim_store"."store_province" IS NULL THEN 1 ELSE 0 END,     "dim_store"."store_province" ASC   select count(distinct "time_year") from "dim_time"  select count(distinct "store_province") from "dim_store"   // 這條SQL在沒有Order的情況是沒有的 select sum("fact_sales"."sales") as "m0" from "fact_sales" as "fact_sales"  // 這條SQL在沒有Order的情況是沒有的 select "dim_store"."store_province" as "c0", sum("fact_sales"."sales") as "m0"  from "dim_store" as "dim_store", "fact_sales" as "fact_sales"  where "fact_sales"."store_id" = "dim_store"."store_id"  group by "dim_store"."store_province"  select "dim_time"."time_year" as "c0", "dim_store"."store_province" as "c1",         sum("fact_sales"."sales") as "m0"  from "dim_time" as "dim_time", "fact_sales" as "fact_sales",         "dim_store" as "dim_store"  where "fact_sales"."time_id" = "dim_time"."time_id"         and "fact_sales"."store_id" = "dim_store"."store_id"   group by "dim_time"."time_year", "dim_store"."store_province" /<code>

8、過濾

Mondrian MDX 執行原理剖析

<code>with member [Measures].[銷售額 環比上季] as 'ParallelPeriod([時間].[季度], (- 1))'select {[Measures].[銷售額], [Measures].[銷售額 環比上季]} ON COLUMNS,  {Filter([時間].[季度].Members, ([Measures].[銷售額] > 4210000))} ON ROWSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>// 獲取明細select "dim_time"."time_year", "dim_time"."time_quarter" from "dim_time" as "dim_time", "fact_sales" as "fact_sales" where "fact_sales"."time_id" = "dim_time"."time_id" group by "dim_time"."time_year", "dim_time"."time_quarter" having (sum("fact_sales"."sales") > 4210000) order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,         "dim_time"."time_year" ASC,     CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_quarter" ASC       select "dim_time"."time_quarter"  from "dim_time" as "dim_time"  where ("dim_time"."time_year" = '2012')  group by "dim_time"."time_quarter"  order by CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_quarter" ASC  select "dim_time"."time_quarter" from "dim_time" as "dim_time"  where ("dim_time"."time_year" = '2013')  group by "dim_time"."time_quarter"  order by CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END,         "dim_time"."time_quarter" ASC   // 獲取年  select "dim_time"."time_year" from "dim_time" as "dim_time"   group by "dim_time"."time_year"   order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_year" ASC    select count(distinct "time_year") from "dim_time"    select count(distinct "time_quarter") from "dim_time"    // 獲取日期明細數據  select "dim_time"."time_year" as "c0", "dim_time"."time_quarter" as "c1",     sum("fact_sales"."sales") as "m0"   from "dim_time" as "dim_time", "fact_sales" as "fact_sales"   where "fact_sales"."time_id" = "dim_time"."time_id"   group by "dim_time"."time_year", "dim_time"."time_quarter"    /<code>


高級計算解析

1、累計分析(YTD/MTD/QTD)

Mondrian MDX 執行原理剖析

<code>with member [Measures].[銷售額 YTD] as 'Sum(Ytd([時間].[季度].CurrentMember), [Measures].[銷售額])'select {[Measures].[銷售額], [Measures].[銷售額 YTD]} ON COLUMNS,  {[時間].[季度].Members} ON ROWSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>// 獲取年-季度select "dim_time"."time_year", "dim_time"."time_quarter" from "dim_time" as "dim_time" group by "dim_time"."time_year", "dim_time"."time_quarter" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,        "dim_time"."time_year" ASC,     CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_quarter" ASC  // 獲取年 select "dim_time"."time_year" from "dim_time" as "dim_time"  group by "dim_time"."time_year"  order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_year" ASC  // 獲取季度 select "dim_time"."time_quarter" from "dim_time" as "dim_time"  where ("dim_time"."time_year" = '2013')  group by "dim_time"."time_quarter"  order by CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_quarter" ASC    select count(distinct "time_year") from "dim_time"    select count(distinct "time_quarter") from "dim_time"    //獲取明細數據  select "dim_time"."time_year" as "c0", "dim_time"."time_quarter" as "c1",     sum("fact_sales"."sales") as "m0"   from "dim_time" as "dim_time", "fact_sales" as "fact_sales"   where "fact_sales"."time_id" = "dim_time"."time_id"   group by "dim_time"."time_year", "dim_time"."time_quarter"/<code>

2、單維度對比

Mondrian MDX 執行原理剖析

<code>with member [Measures].[銷售額 環比上季] as 'ParallelPeriod([時間].[季度], (- 1))'select {[Measures].[銷售額], [Measures].[銷售額 環比上季]} ON COLUMNS,  {[時間].[季度].Members} ON ROWSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>// 獲取年-季度select "dim_time"."time_year", "dim_time"."time_quarter" from "dim_time" as "dim_time" group by "dim_time"."time_year", "dim_time"."time_quarter" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,         "dim_time"."time_year" ASC,     CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_quarter" ASC  // 獲取年select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_year" ASC  // 獲取季度select "dim_time"."time_quarter" from "dim_time" as "dim_time" where ("dim_time"."time_year" = '2013') group by "dim_time"."time_quarter" order by CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END, "dim_time"."time_quarter" ASC    select count(distinct "time_year") from "dim_time"    select count(distinct "time_quarter") from "dim_time"    //獲取明細數據 select "dim_time"."time_year" as "c0", "dim_time"."time_quarter" as "c1",         sum("fact_sales"."sales") as "m0"  from "dim_time" as "dim_time", "fact_sales" as "fact_sales"  where "fact_sales"."time_id" = "dim_time"."time_id"  group by "dim_time"."time_year", "dim_time"."time_quarter"/<code>


3、維度交叉對比

Mondrian MDX 執行原理剖析

<code>with member [Measures].[銷售額 環比上季] as 'ParallelPeriod([時間].[季度], (- 1))'select {Crossjoin([銷售點].[省].Members, {[Measures].[銷售額], [Measures].[銷售額 環比上季]})} ON COLUMNS,  {[時間].[季度].Members} ON ROWSfrom [銷售情況]/<code> 

翻譯成的SQL:

<code>// 獲取省份select "dim_store"."store_province" from "dim_store" as "dim_store" group by "dim_store"."store_province" order by CASE WHEN "dim_store"."store_province" IS NULL THEN 1 ELSE 0 END,         "dim_store"."store_province" ASC  // 獲取季度select "dim_time"."time_year", "dim_time"."time_quarter" from "dim_time" as "dim_time" group by "dim_time"."time_year", "dim_time"."time_quarter" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END,         "dim_time"."time_year" ASC,     CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END,     "dim_time"."time_quarter" ASC  // 獲取年select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END, "dim_time"."time_year" ASC  //select "dim_time"."time_quarter" from "dim_time" as "dim_time" where ("dim_time"."time_year" = '2013') group by "dim_time"."time_quarter" order by CASE WHEN "dim_time"."time_quarter" IS NULL THEN 1 ELSE 0 END, "dim_time"."time_quarter" ASC  select count(distinct "time_year") from "dim_time"    select count(distinct "time_quarter") from "dim_time"    select count(distinct "store_province") from "dim_store"    //獲取明細數據select "dim_time"."time_year" as "c0", "dim_time"."time_quarter" as "c1",         "dim_store"."store_province" as "c2",     sum("fact_sales"."sales") as "m0"  from "dim_time" as "dim_time", "fact_sales" as "fact_sales",         "dim_store" as "dim_store"  where "fact_sales"."time_id" = "dim_time"."time_id"         and "fact_sales"."store_id" = "dim_store"."store_id"  group by "dim_time"."time_year", "dim_time"."time_quarter",         "dim_store"."store_province"/<code>

基於內存的對比

3、總計/小計

Mondrian MDX 執行原理剖析

<code>select {Crossjoin([時間].[年].Members, {[Measures].[銷售額]})} ON COLUMNS,  {Hierarchize({[銷售點].[市].Members, [銷售點].[省].Members})} ON ROWSfrom [銷售情況]/<code>

翻譯成的SQL:

<code>// 獲取年份select "dim_time"."time_year" from "dim_time" as "dim_time" group by "dim_time"."time_year" order by CASE WHEN "dim_time"."time_year" IS NULL THEN 1 ELSE 0 END, "dim_time"."time_year" ASC  // 獲取省份和城市select "dim_store"."store_province", "dim_store"."store_city" from "dim_store" as "dim_store" group by "dim_store"."store_province", "dim_store"."store_city" order by CASE WHEN "dim_store"."store_province" IS NULL THEN 1 ELSE 0 END,     "dim_store"."store_province" ASC,   CASE WHEN "dim_store"."store_city" IS NULL THEN 1 ELSE 0 END,   "dim_store"."store_city" ASC  // 獲取省份select "dim_store"."store_province" from "dim_store" as "dim_store" group by "dim_store"."store_province" order by CASE WHEN "dim_store"."store_province" IS NULL THEN 1 ELSE 0 END,     "dim_store"."store_province" ASCselect count(distinct "time_year") from "dim_time"select count(distinct "store_city") from "dim_store"    //獲取城市明細數據select "dim_time"."time_year" as "c0", "dim_store"."store_city" as "c1",        sum("fact_sales"."sales") as "m0" from "dim_time" as "dim_time", "fact_sales" as "fact_sales", "dim_store" as "dim_store"where "fact_sales"."time_id" = "dim_time"."time_id"         and "fact_sales"."store_id" = "dim_store"."store_id"  group by "dim_time"."time_year", "dim_store"."store_city"// 省份明細數據select "dim_time"."time_year" as "c0", "dim_store"."store_province" as "c1",         sum("fact_sales"."sales") as "m0" from "dim_time" as "dim_time", "fact_sales" as "fact_sales", "dim_store" as "dim_store" where "fact_sales"."time_id" = "dim_time"."time_id"         and "fact_sales"."store_id" = "dim_store"."store_id"  group by "dim_time"."time_year", "dim_store"."store_province"/<code> 
Mondrian MDX 執行原理剖析


分享到:


相關文章: