物化檢視幾個知識點

space6212發表於2019-07-08
源表:物化檢視資料來源對應的表
基表:物化檢視對應的表

本文主要內容包括:
1、如何使源表的資料變化不影響物化檢視的快速重新整理
2、建好物化檢視後,當基表或者源表的結構發生變化對物化檢視重新整理的影響。


測試資料準備:
suk@ORA9I> CREATE TABLE T_MV(C1 NUMBER,C2 NUMBER,PRIMARY KEY(C1));

Table created.

suk@ORA9I> CREATE MATERIALIZED VIEW LOG ON T_MV;

Materialized view log created.

--準備4種方法測試的MV
suk@ORA9I> CREATE MATERIALIZED VIEW MV_1 REFRESH FAST AS SELECT C1,C2 FROM T_MV;

Materialized view created.

suk@ORA9I> CREATE MATERIALIZED VIEW MV_2 REFRESH FAST AS SELECT * FROM T_MV;

Materialized view created.

suk@ORA9I> CREATE MATERIALIZED VIEW MV_3 REFRESH FAST AS SELECT * FROM T_MV T;

Materialized view created.

suk@ORA9I> CREATE MATERIALIZED VIEW MV_4 REFRESH FAST AS SELECT T.* FROM T_MV T;

Materialized view created.

一、如何修改源表資料,而不會產生MLOG$_XXX
物化檢視在快速重新整理時是根據MLOG$_XXX的記錄來決定那些資料需要重新整理的,所以,如果想要源表修改的資料不被重新整理的話,就需要把MLOG$_XXX對應的記錄去掉。
1、用函式包
suk@ORA9I> INSERT INTO T_MV VALUES(1,1);

1 row created.

suk@ORA9I> SELECT * FROM MLOG$_T_MV;

C1 SNAPTIME$ D O CHANGE_VECTO
---------- --------- - - ------------
1 01-JAN-00 I N FE

suk@ORA9I> ROLLBACK;

Rollback complete.

suk@ORA9I> EXEC DBMS_MVIEW.BEGIN_TABLE_REORGANIZATION('SUK','T_MV');
--用這個過程可以使得對源表的DML操作不產生MLOG,影響範圍是從BEGIN_TABLE_REORGANIZATION到END_TABLE_REORGANIZATION其間

PL/SQL procedure successfully completed.

suk@ORA9I> COMMIT;--一定要執行commit,否則還會產生MLOG$

Commit complete.

suk@ORA9I> INSERT INTO T_MV VALUES(1,1);

1 row created.

suk@ORA9I> SELECT COUNT(1) FROM MLOG$_T_MV;

COUNT(1)
----------
0

suk@ORA9I> ROLLBACK;

Rollback complete.

suk@ORA9I> EXEC DBMS_MVIEW.END_TABLE_REORGANIZATION('SUK','T_MV');

PL/SQL procedure successfully completed.

--注意:對於表的DML非常頻繁,如果只是想讓某小部分資料不產生日誌,則這種方法不適合。可以用第二種方法。

2、刪除MLOG$記錄
第二種方法很直接,就是直接刪除不想被重新整理的資料對應的修改日誌。
這種方法的難點是如何準確找出那些是你需要刪除的日誌。方法很麻煩,這裡不詳細說了。

二、如何修改物化檢視資料時,不產生USLOG_XXX
這種情況只能用手工刪除USLOG$_XXX的方法了。

三、源表結構發生變化時
1、源表新增欄位
--新增欄位
suk@ORA9I> ALTER TABLE T_MV ADD(COL3 NUMBER);

Table altered.
--對mv_1進行兩種方法重新整理
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','COMPELETE');

PL/SQL procedure successfully completed.

suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','FAST');

PL/SQL procedure successfully completed.

--對mv_2進行兩種方法重新整理
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_2','COMPELETE');

PL/SQL procedure successfully completed.

suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_2','FAST');

PL/SQL procedure successfully completed.

--對mv_3進行兩種方法重新整理
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_3','COMPELETE');

PL/SQL procedure successfully completed.

suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_3','FAST');

PL/SQL procedure successfully completed.

--對mv_4進行兩種方法重新整理
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_4','COMPELETE');
BEGIN DBMS_MVIEW.REFRESH('MV_4','COMPELETE'); END;

*
ERROR at line 1:
ORA-12018: following error encountered during code generation for "SUK"."MV_4"
ORA-00904: "COL3": invalid identifier
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 794
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 851
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 832
ORA-06512: at line 1


suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_4','FAST');

PL/SQL procedure successfully completed.

--從以上測試結果可以看出,源表新增欄位時,只有MV_4在完全重新整理時會出錯。為什麼會這樣呢?
--先看看MV的DDL:
suk@ORA9I> SELECT DBMS_METADATA.GET_DDL('MATERIALIZED_VIEW','MV_1','SUK') FROM DUAL;

CREATE MATERIALIZED VIEW "SUK"."MV_1"
......
AS SELECT C1,C2 FROM T_MV

suk@ORA9I> C/1/2
1* SELECT DBMS_METADATA.GET_DDL('MATERIALIZED_VIEW','MV_2','SUK') FROM DUAL
suk@ORA9I> /

CREATE MATERIALIZED VIEW "SUK"."MV_2"
......
AS SELECT "T_MV"."C1" "C1","T_MV"."C2" "C2" FROM "T_MV" "T_MV"

suk@ORA9I> C/2/3
1* SELECT DBMS_METADATA.GET_DDL('MATERIALIZED_VIEW','MV_3','SUK') FROM DUAL
suk@ORA9I> /

CREATE MATERIALIZED VIEW "SUK"."MV_3"
......
AS SELECT "T"."C1" "C1","T"."C2" "C2" FROM "T_MV" "T"



suk@ORA9I> C/3/4
1* SELECT DBMS_METADATA.GET_DDL('MATERIALIZED_VIEW','MV_4','SUK') FROM DUAL
suk@ORA9I> /

CREATE MATERIALIZED VIEW "SUK"."MV_4"
......
AS SELECT T.* FROM T_MV T

--看每一個MV的DDL的最後一行,不難發現問題了。
--在前三種情況下,oracle在建立MV時會翻譯成當前源表對應的欄位名;但第四種情況則不然,它是在重新整理時才翻譯成源表對應的欄位,如果源表的結構發生變化,那很明顯,MV重新整理會出現問題。
--那MV_4為什麼快速重新整理就不會出錯呢?透過trace檔案,可以看出完全重新整理和快速重新整理的不同之處:
--完全重新整理
INSERT INTO "SUK"."MV_4"("C1","C2","COL3") SELECT "T"."C1","T"."C2","T"."COL3" FROM "T_MV" "T"
--快速重新整理
INSERT INTO "SUK"."MV_4" ("C1","C2")
VALUES
(:1,:2)
--可以看出,完全重新整理時,是根據源表的結構進行重新整理的
--快速重新整理時,是根據MV的結構進行重新整理的

2、新新增的欄位資料發生變化,快速重新整理是否會重新整理該記錄
suk@ORA9I> SELECT * FROM T_MV;

C1 C2 COL3
---------- ---------- ----------
1 3 3

suk@ORA9I> UPDATE T_MV SET COL3=4;

1 row updated.

suk@ORA9I> COMMIT;

Commit complete.

suk@ORA9I> @begin_trace
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','FAST');
suk@ORA9I> @end_trace

--從trace檔案中可以發現如下語句:
UPDATE "SUK"."MV_1" SET "C1" = :1,"C2" = :2
WHERE
"C1" = :1
--說明在源表中且在MV不存在的欄位的數值發生變化,MV也會重新整理這條資料。且MV的重新整理方式是把整條記錄的所有欄位都更新

3、源表刪除欄位
suk@ORA9I> ALTER TABLE T_MV DROP COLUMN COL3;

Table altered.

suk@ORA9I> ALTER TABLE T_MV DROP COLUMN C2;

Table altered.

suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','COMPELETE');
BEGIN DBMS_MVIEW.REFRESH('MV_1','COMPELETE'); END;

*
ERROR at line 1:
ORA-12008: error in materialized view refresh path
ORA-00904: "T_MV"."C2": invalid identifier
ORA-00904: "C2": invalid identifier
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 794
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 851
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 832
ORA-06512: at line 1


suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','FAST');
BEGIN DBMS_MVIEW.REFRESH('MV_1','FAST'); END;

*
ERROR at line 1:
ORA-12057: materialized view "SUK"."MV_1" is INVALID and must complete refresh
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 794
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 851
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 832
ORA-06512: at line 1

.....MV_1到MV_3都包同樣的錯誤.....

suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_4','COMPELETE');

PL/SQL procedure successfully completed.

suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_4','FAST');

PL/SQL procedure successfully completed.

--其實這個結果可以根據上一步推斷出來了,現在用試驗也證明了。
--根本原因就是建立MV的DDL不同,也就是是否指定*導致的


四、基表結構發生變化時
剛才討論了源表的結構變化對MV重新整理的影響,下面討論基表的結構對MV重新整理的影響。
這個問題相對簡單一點,我們知道,修改基表不會對MV建立的DDL造成影響,也就是不會改變MV的重新整理語句,所以,很容易得到以下結論:
1、如果基表新增欄位,則不會影響快速重新整理和完全重新整理
2、如果基表刪除欄位,則不能快速重新整理和完全重新整理

suk@ORA9I> CREATE TABLE T_MV(C1 NUMBER,C2 NUMBER,PRIMARY KEY(C1));

Table created.

suk@ORA9I> CREATE MATERIALIZED VIEW LOG ON T_MV;

Materialized view log created.

suk@ORA9I> CREATE MATERIALIZED VIEW MV_1 REFRESH FAST AS SELECT C1,C2 FROM T_MV;

Materialized view created.

suk@ORA9I> CREATE MATERIALIZED VIEW MV_4 REFRESH FAST AS SELECT T.* FROM T_MV T;

Materialized view created.
--新增基表欄位
suk@ORA9I> ALTER TABLE MV_1 ADD (C3 NUMBER);

Table altered.

suk@ORA9I> ALTER TABLE MV_4 ADD (C3 NUMBER);

Table altered.
----對mv_1進行兩種方法重新整理
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','COMPELETE');

PL/SQL procedure successfully completed.

suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','FAST');

PL/SQL procedure successfully completed.

--對mv_4進行兩種方法重新整理
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_4','COMPELETE');

PL/SQL procedure successfully completed.

suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_4','FAST');

PL/SQL procedure successfully completed.

--刪除基表欄位
--刪除欄位
suk@ORA9I> ALTER TABLE MV_1 DROP COLUMN C3;

Table altered.

suk@ORA9I> ALTER TABLE MV_1 DROP COLUMN C2;

Table altered.

suk@ORA9I> ALTER TABLE MV_4 DROP COLUMN C3;

Table altered.

suk@ORA9I> ALTER TABLE MV_4 DROP COLUMN C2;

Table altered.

--對mv_1進行兩種方法重新整理
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','COMPELETE');
BEGIN DBMS_MVIEW.REFRESH('MV_1','COMPELETE'); END;

*
ERROR at line 1:
ORA-12008: error in materialized view refresh path
ORA-00904: "C2": invalid identifier
ORA-00904: "C2": invalid identifier
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 794
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 851
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 832
ORA-06512: at line 1


suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_1','FAST');
BEGIN DBMS_MVIEW.REFRESH('MV_1','FAST'); END;

*
ERROR at line 1:
ORA-12057: materialized view "SUK"."MV_1" is INVALID and must complete refresh
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 794
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 851
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 832
ORA-06512: at line 1

--對mv_4進行兩種方法重新整理
suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_4','COMPELETE');
BEGIN DBMS_MVIEW.REFRESH('MV_4','COMPELETE'); END;

*
ERROR at line 1:
ORA-12008: error in materialized view refresh path
ORA-00904: "C2": invalid identifier
ORA-00904: "T"."C2": invalid identifier
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 794
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 851
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 832
ORA-06512: at line 1


suk@ORA9I> EXEC DBMS_MVIEW.REFRESH('MV_4','FAST');
BEGIN DBMS_MVIEW.REFRESH('MV_4','FAST'); END;

*
ERROR at line 1:
ORA-12057: materialized view "SUK"."MV_4" is INVALID and must complete refresh
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 794
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 851
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 832
ORA-06512: at line 1

--以上的測試結果證明了前面的推論實在正確的
五、總結
源表結構變化
1、如果建立MV的DDL用到*(真正儲存在資料庫),則當源表增加欄位時,基於該源表的MV可以正常快速重新整理,但不能完全重新整理;當源表刪除欄位時,基於該源表的MV可以正常快速重新整理,也可以正常完全重新整理。
2、如果建立MV的DDL指定了具體欄位,則當源表增加欄位時欄位時,基於該源表的MV可以正常快速重新整理,也可以正常完全重新整理;當源表刪除欄位時,不能快速重新整理,也不能完全重新整理。
3、源表新增欄位時,發生在新增欄位的資料的變化對應的記錄在快速重新整理時會被重新整理

基表結構變化
3、無論是指定欄位還是用*,如果基表新增欄位,則不會影響快速重新整理和完全重新整理
4、無論是指定欄位還是用*,如果基表刪除欄位,則不能快速重新整理和完全重新整理

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/231499/viewspace-63793/,如需轉載,請註明出處,否則將追究法律責任。

相關文章