本博客是自己在學習和工作途中的積累與總結,僅供自己參考,也歡迎大家轉載,轉載時請注明出處。
http://www.cnblogs.com/king-xg/p/6692841.html
一, model子句
制作表格數據,用傳統sql來實現的話,一般通過多個表的自聯結實現,而model的出現則使得不用自聯結就能實現表格,因為model擁有了跨行應用能力。
(1) 語法
MODEL
[]
[]
[MAIN ]
[PARTITION BY ()]
DIMENSION BY ()
MEASURES ()
[]
[RULES]
(, ,.., )
::=
::= RETURN {ALL|UPDATED} ROWS
::=
[IGNORE NAV | [KEEP NAV]
[UNIQUE DIMENSION | UNIQUE SINGLE REFERENCE]
::=
[UPDATE | UPSERT | UPSERT ALL]
[AUTOMATIC ORDER | SEQUENTIAL ORDER]
[ITERATE () [UNTIL ]]
::= REFERENCE ON ON ()
DIMENSION BY () MEASURES ()
------------建表,初始化數據,才好講下面的內容----------------------
-- 創建表 create table ademo( id number(18) primary key, year varchar2(4), week number(8), sale number(8,2), area varchar2(100) ); -- 創建序列 create sequence seq_ademo_id minvalue 1 start with 1 increment by 1 nomaxvalue nocache nocycle; -- 創建觸發器 create or replace trigger trigger_ademo_id before insert on ademo for each row when (new.id is null) begin select seq_ademo_id.nextval into :new.id from dual; end; -- 初始化數據 insert into ademo (AREA, YEAR, WEEK, SALE) values ('astiya', '2000', 1, 52.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('astiya', '2001', 1, 110.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('astiya', '2001', 2, 110.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('astiya', '2001', 3, 1210.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('astiya', '2002', 1, 170.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('astiya', '2002', 2, 680.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('astiya', '2002', 3, 680.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('anter', '2001', 1, 80.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('anter', '2001', 2, 56.72); insert into ademo (AREA, YEAR, WEEK, SALE) values ('anter', '2001', 3, 156.72); insert into ademo (AREA, YEAR, WEEK, SALE) values ('anter', '2002', 1, 640.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('anter', '2002', 2, 980.12); insert into ademo (AREA, YEAR, WEEK, SALE) values ('anter', '2002', 3, 1980.12); /*delete from ademo;*/ -- 注釋(這是我的個人習慣,不想麻煩的可以不加) comment on table ademo is '測試經濟類的表'; comment on column ademo.id is '主鍵'; comment on column ademo.year is '年份'; comment on column ademo.week is 'xxx周'; comment on column ademo.sale is '銷售額'; comment on column ademo.area is '地區'; -- 展示數據 select * from ademo;
-- 例子1 select year,week,sale,area,up_sale from ademo model return updated rows -- model 語句 partition by (area) -- 分組 dimension by (year,week) -- 維度列 measures(sale,0 up_sale) -- 度量值列 rules( -- 規則 up_sale[year,week]=sale[cv(year),cv(week)]*10, up_sale[1999,1]=100.00 )order by year,week;

-- 例子2 select year,week,sale,area,up_sale from ademo model partition by (area) dimension by (year,week) measures(sale,0 up_sale) rules( up_sale[year,week]=sale[cv(year),cv(week)]*10, up_sale[1999,for week from 1 to 3 increment 1]=100.00 )order by year,week;

--------------------------------------------------------------------
(2) 規則
a. 位置標記
即指定確定的位置明確的維度列值,例如:例子1中的規則(rules)中的up_sale[1999,1]=100.00,明確指出,year=1999,week=1的up_sale列的值為100.00,
作用: 位置標記通常也叫UPSERT,即update and insert,當結果集中不存在則插入,數量隨分組的數量而定;存在時,則更新數據,更新的數據條數同樣與分組的組數相同。
b. 符號標記
即指定范圍的度量列值,例如:例子2中,up_sale[1999,for week from 1 to 3 increment 1]=100.00,指出,week的范圍是在1-3,增長步長為1,所以在每個組中添加了3個up_sale[1999,1..3],共9個。
作用:只能更新數據
(3) model 返回更新后的行
在例子1中,model return updated rows 中,的“return updated rows”表示返回在本次操作中更新或插入的新紀錄。默認返回所有符合條件的記錄
(4) 在model的規則中是能夠使用一般的聚合函數的,例如:count,sum,ave,stddev,PLAP。
(5) model 查找表,功能類似於表連接
---- 查詢表 -- 創建表(銷售表) create table product_cost( id number(18) primary key, year number(4), month number(2), pid number(18), countSum number(18) ); comment on table product_cost is '產品銷售表'; comment on column product_cost.id is '主鍵'; comment on column product_cost.year is '年份'; comment on column product_cost.month is '月份'; comment on column product_cost.pid is '產品id'; comment on column product_cost.countSum is '銷售數量'; -- 創建表(產品表) create table product( id number(18) primary key, pname varchar(100), price number(8,2) ); comment on table product is '產品表'; comment on column product.id is '主鍵'; comment on column product.pname is '產品名稱'; comment on column product.price is '單價'; -- 創建序列 create sequence seq_product_cost_id minvalue 1 start with 1 increment by 1 nomaxvalue nocache nocycle; create sequence seq_product_id minvalue 1 start with 1 increment by 1 nomaxvalue nocache nocycle; -- 創建觸發器 create or replace trigger trigger_product_cost_id before insert on product_cost for each row when (new.id is null) begin select seq_product_cost_id.nextval into :new.id from dual; end; create or replace trigger trigger_product_id before insert on product for each row when (new.id is null) begin select seq_product_id.nextval into :new.id from dual; end; -- 初始化數據 insert into product (pname,price) values('i7-6700K','23'); insert into product (pname,price) values('i7-6600K','20'); insert into product (pname,price) values('i7-6500K','19'); insert into product (pname,price) values('i7-6400K','18'); insert into product (pname,price) values('i7-6300K','17'); insert into product (pname,price) values('i7-6200K','15'); insert into product (pname,price) values('i7-6100K','12'); delete from product; select * from product; insert into product_cost(year,month,pid,countSum) values(2000,1,1,500); insert into product_cost(year,month,pid,countSum) values(2000,1,2,630); insert into product_cost(year,month,pid,countSum) values(2000,1,3,1200); insert into product_cost(year,month,pid,countSum) values(2000,1,4,320); insert into product_cost(year,month,pid,countSum) values(2000,1,5,150); insert into product_cost(year,month,pid,countSum) values(2000,1,6,250); insert into product_cost(year,month,pid,countSum) values(2000,1,7,350); insert into product_cost(year,month,pid,countSum) values(2000,2,1,1500); insert into product_cost(year,month,pid,countSum) values(2000,2,2,1630); insert into product_cost(year,month,pid,countSum) values(2000,2,3,200); insert into product_cost(year,month,pid,countSum) values(2000,2,4,1320); insert into product_cost(year,month,pid,countSum) values(2000,2,5,250); insert into product_cost(year,month,pid,countSum) values(2000,2,6,350); insert into product_cost(year,month,pid,countSum) values(2000,2,7,450); insert into product_cost(year,month,pid,countSum) values(2000,3,1,520); insert into product_cost(year,month,pid,countSum) values(2000,3,2,660); insert into product_cost(year,month,pid,countSum) values(2000,3,3,1900); insert into product_cost(year,month,pid,countSum) values(2000,3,4,300); insert into product_cost(year,month,pid,countSum) values(2000,3,5,180); insert into product_cost(year,month,pid,countSum) values(2000,3,6,210); insert into product_cost(year,month,pid,countSum) values(2000,3,7,320); insert into product_cost(year,month,pid,countSum) values(2000,4,1,1520); insert into product_cost(year,month,pid,countSum) values(2000,4,2,1660); insert into product_cost(year,month,pid,countSum) values(2000,4,3,2900); insert into product_cost(year,month,pid,countSum) values(2000,4,4,1200); insert into product_cost(year,month,pid,countSum) values(2000,4,5,980); insert into product_cost(year,month,pid,countSum) values(2000,4,6,910); insert into product_cost(year,month,pid,countSum) values(2000,4,7,620); insert into product_cost(year,month,pid,countSum) values(2001,1,1,500); insert into product_cost(year,month,pid,countSum) values(2001,1,2,630); insert into product_cost(year,month,pid,countSum) values(2001,1,3,1200); insert into product_cost(year,month,pid,countSum) values(2001,1,4,320); insert into product_cost(year,month,pid,countSum) values(2001,1,5,150); insert into product_cost(year,month,pid,countSum) values(2001,1,6,250); insert into product_cost(year,month,pid,countSum) values(2001,1,7,350); insert into product_cost(year,month,pid,countSum) values(2001,2,1,1500); insert into product_cost(year,month,pid,countSum) values(2001,2,2,1630); insert into product_cost(year,month,pid,countSum) values(2001,2,3,200); insert into product_cost(year,month,pid,countSum) values(2001,2,4,1320); insert into product_cost(year,month,pid,countSum) values(2001,2,5,250); insert into product_cost(year,month,pid,countSum) values(2001,2,6,350); insert into product_cost(year,month,pid,countSum) values(2001,2,7,450); insert into product_cost(year,month,pid,countSum) values(2001,3,1,520); insert into product_cost(year,month,pid,countSum) values(2001,3,2,660); insert into product_cost(year,month,pid,countSum) values(2001,3,3,1900); insert into product_cost(year,month,pid,countSum) values(2001,3,4,300); insert into product_cost(year,month,pid,countSum) values(2001,3,5,180); insert into product_cost(year,month,pid,countSum) values(2001,3,6,210); insert into product_cost(year,month,pid,countSum) values(2001,3,7,320); insert into product_cost(year,month,pid,countSum) values(2001,4,1,1520); insert into product_cost(year,month,pid,countSum) values(2001,4,2,1660); insert into product_cost(year,month,pid,countSum) values(2001,4,3,2900); insert into product_cost(year,month,pid,countSum) values(2001,4,4,1200); insert into product_cost(year,month,pid,countSum) values(2001,4,5,980); insert into product_cost(year,month,pid,countSum) values(2001,4,6,910); insert into product_cost(year,month,pid,countSum) values(2001,4,7,620); select * from product_cost; select * from product;
在傳統sql實現:
select pc.year as year,pc.month as month, p.pname as pname,pc.countSum as count, (pc.countSum * p.price) as sale
from product_cost pc left join product p
on pc.pid=p.id
where year=2000 and month=4;
-- 解釋計划
explain plan for
select pc.year as year,pc.month as month, p.pname as pname,pc.countSum as count, (pc.countSum * p.price) as sale
from product_cost pc left join product p
on pc.pid=p.id where year=2000 and month=4;
commit;
-- 查看解釋計划
select * from table(dbms_xplan.display);

由於沒添加索引所以全盤掃描。
model查找表:
select year,month,pid,pname,price,sale,countSum
from product_cost
model
reference ref_pro on
(
select id,pname,price
from product
)
dimension by (id)
measures (pname,price)
main main_selection
partition by (year,month)
dimension by (pid)
measures(countSum,cast(' ' as varchar2(200))pname, cast(0 as number(18,2))sale, cast(0 as number(8,2))price)
rules (
pname[pid] =ref_pro.pname[cv(pid)],
price[pid]=ref_pro.price[cv(pid)],
countSum[pid]=countSum[cv(pid)],
sale[pid]=price[cv(pid)]*countSum[cv(pid)]
) where year=2000 and month=4 order by year,month,pid;
-- 解釋計划
explain plan for
select year,month,pid,pname,price,sale,countSum
from product_cost
model
reference ref_pro on
(
select id,pname,price
from product
)
dimension by (id)
measures (pname,price)
main main_selection
partition by (year,month)
dimension by (pid)
measures(countSum,cast(' ' as varchar2(200))pname, cast(0 as number(18,2))sale, cast(0 as number(8,2))price)
rules (
pname[pid] =ref_pro.pname[cv(pid)],
price[pid]=ref_pro.price[cv(pid)],
countSum[pid]=countSum[cv(pid)],
sale[pid]=price[cv(pid)]*countSum[cv(pid)]
) where year=2000 and month=4 order by year,month,pid;
commit;
-- 查看解釋計划
select * from table(dbms_xplan.display);

兩者相比較,model子句的性能會更好,即便在沒有索引的情況下,model子句預期訪問的字節數要小於傳統的sql自聯結,那這是為什么呢?
其實這與model的內部分組機制有關,謂語中的字段含有分組(partition by)中的字段,所以,model就會僅僅訪問謂語指定的分區,其他分區不管,這很大程度上提高了sql的性能。
(5)謂語前推
--謂語前推
-- 內嵌視圖
select *
from ( select year,week,sale,area,0 as new_sale from ademo)
model return updated rows
partition by (year,week)
dimension by (area)
measures (sale, 0 new_sale)
rules (
new_sale[area]=sale[cv(area)]*10
)
order by year,week;
-- 成功將謂語推入視圖
explain plan for
select *
from ( select year,week,sale,area,0 as new_sale from ademo
model return updated rows
partition by (year,week)
dimension by (area)
measures (sale, 0 new_sale)
rules (
new_sale[area]=sale[cv(area)]*10
)
)where year=2001
order by year,week;
commit;
-- 解釋計划
select * from table(dbms_xplan.display);

在一開始全表掃描的時候就執行了過濾,減少了掃描的數據塊的數,降低了加載的字節數。
然后看看下面推入失敗的sql
-- 失敗,在全表掃描完后的結果集上進行過濾,並未退入到視圖
explain plan for
select *
from ( select year,week,sale,area,0 as new_sale from ademo
model return updated rows
partition by (year,week)
dimension by (area)
measures (sale, 0 new_sale)
rules (
new_sale[area]=sale[cv(area)]*10
)
)where area='kinggardom'
order by year,week;
commit;
-- 解釋計划
select * from table(dbms_xplan.display);

很明顯,過濾實在view操作的時候進行,即在得到全包掃描后的結果集后進行過濾,無疑說明此次謂語前推失敗。
-- 原因:在model中存在一種分區的機制,partition by是進行分區的判斷依據,那么若果在外sql中存在與分區列匹配的列,則model子句就會只掃描匹配的分區,其他分區就不管了,如果不存在則,全表掃描或者說掃描所有分區
-- 結論: 謂語中,能被推入到視圖中的僅僅只有分組中的字段(partition by(字段))
(6) 子查詢因子化(小小的提一下,后期再出詳細的筆記)
-- 格式: with [alias] as () select ...
-- 題目: 將同一年的一月前的sale進行對比,查看是增長還是下降了多少
-- 神似內嵌視圖
舉例:
with t as ( select year,month,pid,pname,price,sale,countSum from product_cost model reference ref_pro on ( select id,pname,price from product ) dimension by (id) measures (pname,price) main main_selection partition by (year,month) dimension by (pid) measures(countSum,cast(' ' as varchar2(200))pname, cast(0 as number(18,2))sale, cast(0 as number(8,2))price) rules ( pname[pid] =ref_pro.pname[cv(pid)], price[pid]=ref_pro.price[cv(pid)], countSum[pid]=countSum[cv(pid)], sale[pid]=price[cv(pid)]*countSum[cv(pid)] ) ) select year,month,pname,sale,pre_sale,compare_pre_sale from t model partition by (pname) dimension by (year,month) measures (0 pre_sale,0 compare_pre_sale,sale) rules( pre_sale[year,month]=presentnnv(sale[cv(year),cv(month)-1],sale[cv(year),cv(month)-1],sale[cv(year),cv(month)]), compare_pre_sale[year,month]=sale[cv(year),cv(month)]-pre_sale[cv(year),cv(month)] )order by pname,month;

其實這個可以不用把規則分開寫,因為數據並不復雜,對於復雜的數據,用這個還是很不錯的選擇。
小結: model用來制作表格數據比之傳統的表聯結來實現會是一個更好的選擇,model子句能夠提供更好的sql性能,提供更清晰的結構。
