---
# System prepended metadata

title: Oracle MODEL
tags: [MODEL, oracle]

---

# 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]