前些天和space6212讨论了一下这个问题,这里简单记录一下。
首先看看下面两个物化视图有什么区别:
SQL> CREATE TABLE T (ID NUMBER PRIMARY KEY, NAME VARCHAR2(30));
表已创建。
SQL> CREATE MATERIALIZED VIEW LOG ON T;
实体化视图日志已创建。
SQL> CREATE MATERIALIZED VIEW MV_T1 REFRESH FAST AS SELECT * FROM T;
实体化视图已创建。
SQL> CREATE MATERIALIZED VIEW MV_T2 REFRESH FAST AS SELECT T.* FROM T;
实体化视图已创建。
唯一的区别在于MV_T2在SELECT *的时候指明了T.*,不要小看这点区别,就是T.*和*的区别,就会导致两个物化视图在刷新时的表现截然不同。
为了展示问题,需要对基表添加一列,这对于普通的物化视图来说不是问题。物化视图日志会自动支持新增的列。
SQL> INSERT INTO T VALUES (1, 'YTK');
已创建 1 行。
SQL> ALTER TABLE T ADD AGE NUMBER(3);
表已更改。
SQL> EXEC DBMS_MVIEW.REFRESH('MV_T1')
PL/SQL 过程已成功完成。
SQL> EXEC DBMS_MVIEW.REFRESH('MV_T2')
PL/SQL 过程已成功完成。
快速刷新两个物化视图都没有产生问题,那么看看完全刷新的情况:
SQL> EXEC DBMS_MVIEW.REFRESH('MV_T1', 'C')
PL/SQL 过程已成功完成。
SQL> EXEC DBMS_MVIEW.REFRESH('MV_T2', 'C')
BEGIN DBMS_MVIEW.REFRESH('MV_T2', 'C'); END;
*
ERROR 位于第 1 行:
ORA-12018: 在创建"YANGTK"."MV_T2" 的代码时出现以下错误
ORA-00904: "AGE": 无效的标识符
ORA-06512: 在"SYS.DBMS_SNAPSHOT", line 794
ORA-06512: 在"SYS.DBMS_SNAPSHOT", line 851
ORA-06512: 在"SYS.DBMS_SNAPSHOT", line 832
ORA-06512: 在line 1
差异终于体现了。MV_T1的完全刷新没有任何问题,而对于MV_T2,完全刷新导致了错误的产生。
这个测试说明了两个问题。首先,两个物化视图是有区别的,否则不会一个在完全刷新时报错,而另一个在完全刷新时不报错。其次,物化视图的快速刷新和完全刷新的方法并不一样,否则不会快速刷新正常而完全刷新报错。
www.ixdba.net
先看第一个问题:
SQL> COL QUERY FORMAT A80
SQL> SELECT MVIEW_NAME, QUERY FROM USER_MVIEWS WHERE MVIEW_NAME LIKE 'MV_T_';
MVIEW_NAME QUERY
------------------------------ ------------------------------------------------
MV_T1 SELECT "T"."ID" "ID","T"."NAME" "NAME" FROM "T" "T"
MV_T2 SELECT T.* FROM T
由于MV_T2在*前面添加了T,物化视图的定义并不像MV_T1那样被全部解析为列的列表方式。在完全刷新的时候,MV_T2根据T重新展开,而此时由于增加了新的列,导致物化视图同步时查询列和插入列不匹配。
而MV_T1则没有这个问题,MV_T1已经在创建物化视图的时候将列定义展开,因此新增的字段对MV_T1没有任何影响。
最后看看快速刷新和完全刷新的区别,在*前面添加了T之后,物化视图的定义并没有被展开。而物化视图快速刷新要顺利完成,就要求数据字典中必须有专门的地方来保留快速刷新需要使用的SQL语句:
SQL> ALTER SESSION SET SQL_TRACE = TRUE;
会话已更改。
SQL> EXEC DBMS_MVIEW.REFRESH('MV_T1')
PL/SQL 过程已成功完成。
SQL> ALTER SESSION SET SQL_TRACE = FALSE;
会话已更改。
TRACE文件内容比较长,下面仅摘录关键部分:
PARSING IN CURSOR #10 len=249 dep=1 uid=0 oct=3 lid=0 tim=18446744072015785716 hv=2055365415 ad='7db80b40'
SELECT operation#, cols, sql_txt, tabnum, fcmaskvec, ejmaskvec, setnum FROM sys.snap_refop$ WHERE ((operation# >= 0 AND operation# <= 6) OR operation# = 10) AND sowner = :1 AND vname = :2 AND instsite = :3 ORDER BY tabnum, setnum, operation#