# Oracle MODEL
###### tags: `oracle` `MODEL`
## 語法
```sql=
MODEL [RETURN [UPDATED | ALL] ROWS]
[reference models]
[PARTITION BY (<cols>)]
DIMENSION BY (<cols>)
MEASURES (<cols>) [IGNORE NAV] | [KEEP NAV]
[RULES
[UPSERT | UPDATE]
[AUTOMATIC ORDER | SEQUENTIAL ORDER]
```
### 語法說明
MODEL:是一個宣告的關鍵字
PARTITION BY:以XX欄位為分組
DIMENSION BY:MODEL維度設定,看成INDEX,可以是複合PK
MEASURES:指定資料欄位,可自行定義
RULES:規則,你怎麼去操作它,如any,cv..
## 範例
### 範例_MODEL
[範例來源](http://www.mincoder.com/article/1724.shtml)
```sql=
CREATE TABLE A AS
SELECT 'lottu' AS vname,
1 AS vals FROM dual;
SELECT vname,vals FROM A
MODEL
--partition by ()可以忽略
DIMENSION BY(vals)
MEASURES(vname)
RULES (vname[1]='0924');
```
結果如下:
```
VNAME VALS
0924 1
```
執行結果會發現,vname的部份被指定為0924,因為只有一筆資料,所以index(VALS)=1的部份即為該資料,而且被指定為0924!
如果調整一下RULES!
```sql=
SELECT vname,vals FROM A
MODEL
--partition by ()可以忽略
DIMENSION BY(vals)
MEASURES(vname)
RULES (vname[0]='0924');
```
結果如下:
```
VNAME VALS
1 lottu 1
2 0924 0
```
這時候會發現,多了一筆資料了,並且VALS為0!
我們再插入一筆資料,('LI',2)
```sql=
INSERT INTO A VALUES ('LI',2);
COMMIT;
```
接著執行!
```sql=
SELECT vname,vals FROM A
MODEL
DIMENSION BY(vals)
MEASURES(vname)
RULES (vname[2]='0924');
```
結果如下:
```
VNAME VALS
1 lottu 1
2 0924 2
```
跟剛才一樣,RULES將LI調整為0924了!
當然也可以跟剛才不一樣,用不存在的INDEX去做設置。
```sql=
SELECT vname,vals FROM A
MODEL
DIMENSION BY(vals)
MEASURES(vname)
RULES (vname[5]='0924',vname[0]='99');
```
這次我們加了兩筆記錄進去。
### 範例_MODEL RETURN UPDATED ROWS_1
MODEL後面如果加上RETURN UPDATED ROWS即代表,**有被RULES更新或者插入的資料才會顯示**。
```sql=
SELECT vname,vals FROM A
MODEL RETURN UPDATED ROWS
DIMENSION BY(vals)
MEASURES(vname)
RULES (vname[0]='0924');
```
結果如下:
```
VNAME VALS
1 0924 0
```
我們有兩筆資料,按上面的練習應該是會出現三筆才對!
但這次的SELECT卻只出現一筆,這就是加入RETURN UPDATED ROWS的用途!
### 範例_MODEL RETURN UPDATED ROWS_2_加總
用另一個例子來說明!
建立另一個新的table,並加入數據!
我們建立了2011年到2014年的資料,希望預測2015年!
```sql=
CREATE TABLE B(p_id NUMBER,p_year Varchar2(5),p_val NUMBER);
INSERT INTO B VALUES (1001,'2011',25);
INSERT INTO B VALUES (1001,'2012',35);
INSERT INTO B VALUES (1001,'2013',65);
INSERT INTO B VALUES (1001,'2014',95);
INSERT INTO B VALUES (1002,'2011',25);
INSERT INTO B VALUES (1002,'2012',55);
INSERT INTO B VALUES (1002,'2013',75);
INSERT INTO B VALUES (1002,'2014',95);
```
接著先以不加入RETURN的方式呈現比較清楚整個資料結構。
```sql=
SELECT * FROM B
MODEL
PARTITION BY (p_id)
DIMENSION BY (p_year)
MEASURES (p_val)
RULES (p_val['2015']=p_val['2014']+p_val['2013']);
```
結果如下:
```
P_ID P_YEAR P_VAL
1 1001 2011 25
2 1001 2012 35
3 1001 2013 65
4 1001 2014 95
5 1002 2011 25
6 1002 2012 55
7 1002 2013 75
8 1002 2014 95
9 1001 2015 160
10 1002 2015 170
```
我們以P_ID為分組依據,以P_YEAR為維度,設定P_VAL為呈現的數據,然後設置2015年的值=2013年加上2014年!
接著我們加入RETURN UPDATED ROWS
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
PARTITION BY (p_id)
DIMENSION BY (p_year)
MEASURES (p_val)
RULES (p_val['2015']=p_val['2014']+p_val['2013']);
```
結果如下:
```
P_ID P_YEAR P_VAL
1 1001 2015 160
2 1002 2015 170
```
只回傳異動的資料,所以只會有2015年的資料呈現。
### 範例_MODEL RETURN UPDATED ROWS_3_個別處理
資料集的部份一樣是剛才建置的TABLE B
2015年的1001是前兩年的總合,而1002是上年度的2倍。
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year)
MEASURES (p_val)
RULES (p_val[1001,'2015']=p_val[1001,'2013']+p_val[1001,'2014'],
P_val[1002,'2015']=2 * p_val[1002,'2014']);
```
結果如下:
```
P_ID P_YEAR P_VAL
1 1002 2015 190
2 1001 2015 160
```
這次不設置PARTITION
將P_ID加入維度內(DIMENSION)
並且搜尋數據組一樣為P_VAL
就可以個別的處理兩個P_ID的2015年的銷售計算了。
### 範例_MODEL RETURN UPDATED ROWS_4_RULES BETWEEN AND
:::info
語法:SUM(MEASURES)[DIMENSION BETWEEN CON1 AND CON2]
:::
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
PARTITION BY (p_id)
DIMENSION BY (p_year)
MEASURES (p_val)
RULES (p_val['2015']=sum(p_val)[p_year BETWEEN '2013' AND '2014']);
```
結果如下:
```
P_ID P_YEAR P_VAL
1 1001 2015 160
2 1002 2015 170
```
透過SUM(**MEASURES**)[**DIMENSION** **BETWEEN** CON1 **AND** CON2]
可以像在操作標準SQL語法一樣設置條件。
此例加總了2013年至2014年的P_VAL
### 範例_MODEL RETURN UPDATED ROWS_5_RULES FOR
:::info
語法:SUM(MEASURES)[FOR DIMENSION IN (CONDITION)]
:::
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
PARTITION BY (p_id)
DIMENSION BY (p_year)
MEASURES (p_val)
RULES (p_val['2015']=sum(p_val)[for p_year in ('2014','2013')]);
```
結果如下:
```
P_ID P_YEAR P_VAL
1 1001 2015 160
2 1002 2015 170
```
這個案例我們用了FOR + IN的語法
FOR DIMENSION IN 條件
所以一樣求得了2013年與2014年的加總
如果P_YEAR本身是數值的話,可以利用表達式
:::info
FOR DIMENSION FROM INT1 TO INT2 INCREMENT N
:::
此例來說,如果P_YEAR為數值,那我們的表達式可以以這樣子來表示
```sql=
for year from 2013 to 2014 increment 1
```
代表從2013年到2014年,迭代部份一次增加1
**最後提到**
:::success
FOR DIMENSION IN (SELECT 子句)
:::
在IN的部份是可以利用SELECT子句來處理
### 範例_MODEL RETURN UPDATED ROWS_6_RULES ANY, ISNAY
ANY>位置標記使用
IS ANY>符號標記使用
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
PARTITION BY (p_id)
DIMENSION BY (p_year)
MEASURES (p_val)
RULES (p_val['2017']=SUM(p_val)[ANY]);
```
結果如下:
```
P_ID P_YEAR P_VAL
1 1001 2017 220
2 1002 2017 250
```
我們假設2017年的預測為前面幾年的部份,這時候可以利用ANY表達式!
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
PARTITION BY (p_id)
DIMENSION BY (p_year)
MEASURES (p_val)
RULES (p_val['2017']=SUM(p_val)[P_YEAR IS ANY]);
```
也可以得到一樣的結果
### 範例_MODEL RETURN UPDATED ROWS_7_RULES CURRENTV()
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year)
MEASURES (p_val)
RULES (p_val[1001,'2015']=p_val[currentv(),'2013']+p_val[currentv(),'2014'],
P_val[1002,'2015']=2 * p_val[currentv(),'2014']);
```
結果如下:
```
P_ID P_YEAR P_VAL
1 1002 2015 190
2 1001 2015 160
```
CURRENTV主要用來取得DIMENSION目前的值!
對比上面的範例
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year)
MEASURES (p_val)
RULES (p_val[1001,'2015']=p_val[1001,'2013']+p_val[1001,'2014'],
P_val[1002,'2015']=2 * p_val[1002,'2014']);
```
我們透過currentv()去取得了DIMENSION目前各別為1001與1002
### 範例_MODEL RETURN UPDATED ROWS_8_RULES CURRENTV()+FOR
加入月份,結合FOR與CURRENTV()
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year,p_month)
MEASURES (p_val)
RULES (
p_val[1001,'2015', FOR P_MONTH FROM 1 TO 4 INCREMENT 1]=p_val[1001,'2013',CURRENTV()]+p_val[1001,'2014',CURRENTV()]
);
```
結果如下:
```
P_ID P_YEAR P_MONTH P_VAL
1 1001 2015 1 160
2 1001 2015 2 160
3 1001 2015 3 160
4 1001 2015 4 160
```
以FOR來迭代月份,再以CURRENTV()來取得目前的迭代月份
### 範例_MODEL RETURN UPDATED ROWS_9_RULES IS PRESENT
IS PRESENT主要用來判斷數據組是否存在!
大概就是IS NULL快差不多道理了,只是如果存在的話就回傳TRUE。
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year,p_month)
MEASURES (p_val)
RULES (
p_val[1001,'2015', FOR P_MONTH FROM 1 TO 5 INCREMENT 1]=
CASE WHEN p_val[1001,'2013',CURRENTV()] IS PRESENT THEN
p_val[1001,'2013',CURRENTV()]+p_val[1001,'2014',CURRENTV()]
ELSE
500 END
);
```
結果如下:
```
P_ID P_YEAR P_MONTH P_VAL
1 1001 2015 1 160
2 1001 2015 2 160
3 1001 2015 3 160
4 1001 2015 4 160
5 1001 2015 5 500
```
在句中加入了CASE判斷,如果TRUE,那就以數據加總,否則為500。
我讓迭代1到5,但是資料庫內並沒有5月的資料,所以5月的部份就預設為500了。
### 範例_MODEL RETURN UPDATED ROWS_10_RULES PRESENTNNV()
PRESENTNNV()的用法,就跟SSRS的IIF觀念一樣,
PRESENTNNV(cell,expr1,expr2) 回傳表達式expr1,如果行不存在或單元值為空值,返回表达式 expr2
```sql=
SELECT * FROM B
MODEL RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year,p_month)
MEASURES (p_val)
RULES (
p_val[1001,'2015', FOR P_MONTH FROM 1 TO 5 INCREMENT 1]=
PRESENTNNV(p_val[1001,'2013',CURRENTV()], --CELL
p_val[1001,'2013',CURRENTV()]+p_val[1001,'2014',CURRENTV()], --EXPR1
500) --EXPR2
);
```
結果如下:
```
P_ID P_YEAR P_MONTH P_VAL
1 1001 2015 1 160
2 1001 2015 2 160
3 1001 2015 3 160
4 1001 2015 4 160
5 1001 2015 5 500
```
將上面的CASE子句調整成以PRESENTNNV()來處理,得到一樣的結果。
### 範例_MODEL RETURN UPDATED ROWS_11_RULES IGNORE NAV、KEEP NAV
基本上,ORACLE將空值視為NULL,所以沒有像MSSQL一樣,可以用' '來判斷是否為空值,只能IS NULL。
所以,在MODEL內可以透過IGNORE來改變!
當數值欄位為空,則視為0
當字串欄位為空,則視為空格
當日期欄位為空,則視為01-JAN-2000
KEEP NAV的部份已是預設狀況,不多說明。
:::warning
注意IGNORE NAV的放置點
:::
```sql=
SELECT * FROM B
MODEL IGNORE NAV RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year,p_month)
MEASURES (p_val)
RULES (
p_val[1001,'2015', FOR P_MONTH FROM 1 TO 5 INCREMENT 1]=
p_val[1001,'2013',CURRENTV()]+p_val[1001,'2014',CURRENTV()]
);
```
範例9、範例9、10、11都是用來處理NULL的。
### 範例_MODEL RETURN UPDATED ROWS_7_RULES cv()
cv的應用在於,可以指定以RULES等式左側的目標式的當前維度值在右等式右側操作!
以關聯式資料庫來看待的話,cv可以當做是關聯操作,很抽像!
看例子!
```sql=
SELECT * FROM B
MODEL IGNORE NAV RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year,p_month)
MEASURES (p_val)
RULES (
p_val[1001,'2015', FOR P_MONTH FROM 1 TO 5 INCREMENT 1]=
p_val[1001,CV(p_year),CV(p_month)]
);
```
結果如下:
```
P_ID P_YEAR P_MONTH P_VAL
1 1001 2015 1 0
2 1001 2015 2 0
3 1001 2015 3 0
4 1001 2015 4 0
5 1001 2015 5 0
```
cv的應用,<font color=red >**在對左側的目標式的當前維度值**</font>在右側操作!
此例中,當前的DIMENSION是『<font color=red>2015</font>』
而2015年在我們的範例資料表內是沒有資料的,所以p_val的值皆為0。
接著,我們調整cv
```sql=
SELECT * FROM B
MODEL IGNORE NAV RETURN UPDATED ROWS
DIMENSION BY (p_id,p_year,p_month)
MEASURES (p_val)
RULES (
p_val[1001,'2015', FOR P_MONTH FROM 1 TO 5 INCREMENT 1]=
p_val[1001,CV(p_year)-1,CV(p_month)]
);
```
然後我們先看一下資料表的資料
```sql=
SELECT * FROM b WHERE p_year=2014 AND p_id=1001;
```
結果如下:
```
P_ID P_YEAR P_MONTH P_VAL
1 1001 2014 95 2
2 1001 2014 95 3
3 1001 2014 95 4
4 1001 2014 95 1
```
MODEL查詢結果如下:
```
P_ID P_YEAR P_MONTH P_VAL
1 1001 2015 1 95
2 1001 2015 2 95
3 1001 2015 3 95
4 1001 2015 4 95
5 1001 2015 5 0
```
我在cv(p_year)-1,代表著要以2015-1=2014的年份來做關聯!
就得到上面的結果了,以2014年的p_val來統計。
所以,透過cv()可以操作DIMENSION條件來做等式右側的資料統計
> [time=Thu, Nov 30, 2017 1:40 PM]
> [name=shaoeChen]