Sunday, September 13, 2009

Fast refreshable materialized view errors, part six: MV_CAPABILITIES_TABLE

After you've been confronted with an error message during the creation of your fast refreshable materialized view, you either know exactly what to do next to resolve the error, or not. If not, the MV_CAPABILITIES_TABLE might help. You can read about it in the documentation here. I remember trying to use it in my project half a year ago and I didn't find it useful. After reading this post, you'll probably know what error message I was trying to resolve back then...

The MV_CAPABILITIES_TABLE is much like the PLAN_TABLE. The latter stores the execution plan of a SQL query and its related statistics after issuing an explain plan statement. The MV_CAPABILITIES_TABLE stores the capabilities, among which the capability to fast refresh, after issuing an dbms_mview.explain_mview statement. You can install the MV_CAPABILITIES_TABLE by running the utlxmv.sql script.

Here is how it works when the fast refreshable materialized view doesn't experience any problems. It is the same setup as in part one:

rwijk@ORA11GR1> create table myemp as select * from emp
2 /

Table created.

rwijk@ORA11GR1> alter table myemp add primary key (empno)
2 /

Table altered.

rwijk@ORA11GR1> create materialized view log on myemp
2 with primary key, rowid
3 /

Materialized view log created.

rwijk@ORA11GR1> truncate table mv_capabilities_table
2 /

Table truncated.

Now wrap the entire "create materialized view" statement as an argument to the explain_mview procedure:

rwijk@ORA11GR1> begin
2 dbms_mview.explain_mview
3 ( 'create materialized view emp_mv
4 refresh fast on commit
5 as
6 select empno
7 , ename
8 , job
9 , sal
10 , deptno
11 from myemp
12 where deptno = 10'
13 );
14 end;
15 /

PL/SQL procedure successfully completed.

rwijk@ORA11GR1> select *
2 from mv_capabilities_table
3 order by seq
4 /

STATEMENT_ID MVOWNER
------------------------------ ------------------------------
MVNAME CAPABILITY_NAME P
------------------------------ ------------------------------ -
RELATED_TEXT
--------------------------------------------------------------------------------
RELATED_NUM MSGNO
----------- ----------
MSGTXT
--------------------------------------------------------------------------------
SEQ
----------
RWIJK
EMP_MV PCT N



1

RWIJK
EMP_MV REFRESH_COMPLETE Y



1002

RWIJK
EMP_MV REFRESH_FAST Y



2003

RWIJK
EMP_MV REWRITE N



3004

RWIJK
EMP_MV PCT_TABLE N
MYEMP
155 2068
relation is not a partitioned table
4005

RWIJK
EMP_MV REFRESH_FAST_AFTER_INSERT Y



5006

RWIJK
EMP_MV REFRESH_FAST_AFTER_ONETAB_DML Y



6007

RWIJK
EMP_MV REFRESH_FAST_AFTER_ANY_DML Y



7008

RWIJK
EMP_MV REFRESH_FAST_PCT N

2157
PCT is not possible on any of the detail tables in the materialized view
8009

RWIJK
EMP_MV REWRITE_FULL_TEXT_MATCH N

2159
query rewrite is disabled on the materialized view
9010

RWIJK
EMP_MV REWRITE_PARTIAL_TEXT_MATCH N

2159
query rewrite is disabled on the materialized view
10011

RWIJK
EMP_MV REWRITE_GENERAL N

2159
query rewrite is disabled on the materialized view
11012

RWIJK
EMP_MV REWRITE_PCT N

2158
general rewrite is not possible or PCT is not possible on any of the detail tabl
es
12013

RWIJK
EMP_MV PCT_TABLE_REWRITE N
MYEMP
155 2068
relation is not a partitioned table
13014


14 rows selected.

This materialized view is fast refreshable. You can tell because the column "Possible" contains a 'Y' value for the REFRESH_FAST% capabilities.

The MV capabilities are roughly about three features: fast refresh, query rewrite and partition change tracking (wonderful feature, by the way!). This series is about fast refresh, so from now on I'll use the predicate "capability_name like 'REFRESH_FAST%'".

I will now repeat all situations of the previous five posts, only skipping the ones that didn't result in an error. You can imagine it can become extremely lengthy, so I'll only write the restriction, the error message and the result of doing this sequence:

truncate table mv_capabilities_table
/
begin
dbms_mview.explain_mview
( 'create materialized view ...'
);
end;
/
select capability_name
, possible
, msgtxt
from mv_capabilities_table
where capability_name like 'REFRESH_FAST%'
order by seq
/

At the end of this post, you'll find a link to the script used to receive this output, so you can replay it on your own database, and see for yourself.


Restriction 1 for basic MV fast refresh: The materialized view must not contain references to non-repeating expressions like SYSDATE and ROWNUM.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: the dbms_mview.explain_mview errors out with an ORA-32342:
rwijk@ORA11GR1> begin
2 dbms_mview.explain_mview
3 ( 'create materialized view emp_mv
4 refresh fast on commit
5 as
6 select empno
7 , ename
8 , job
9 , sal
10 , deptno
11 , sysdate now
12 from myemp
13 where deptno = 10'
14 );
15 end;
16 /
begin
*
ERROR at line 1:
ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view
statement
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view
ORA-06512: at "SYS.DBMS_XRWMV", line 22
ORA-06512: at "SYS.DBMS_XRWMV", line 42
ORA-06512: at "SYS.DBMS_SNAPSHOT", line 3299
ORA-06512: at line 2

And the MV_CAPABILITIES TABLE remains empty:

rwijk@ORA11GR1> select capability_name
2 , possible
3 , msgtxt
4 from mv_capabilities_table
5 where capability_name like 'REFRESH_FAST%'
6 order by seq
7 /

no rows selected
This situation will be encountered much more often, so this will be shortened to "ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement" from now on.



Restriction 3 for basis MV fast refresh: It cannot contain a SELECT list subquery.

Error message: ORA-22818: subquery expressions not allowed here

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 4 for basic MV fast refresh: It cannot contain an analytic function.

Error message: ORA-12052: cannot fast refresh materialized view RWIJK.EMP_MV

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
window function in mv

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view

The "ORA-12052: cannot fast refresh materialized view X" does not tell you why it cannot refresh, but the MV capabilities table tells you "window function in MV" as the reason. So it's worth using in this case.



Restriction 5 for basic MV fast refresh: It cannot contain a MODEL clause.

Error message: ORA-12052: cannot fast refresh materialized view RWIJK.EMP_MV

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
a SPREADSHEET clause is present

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view

"a SPREADSHEET clause is present". Nice name for the model clause, but again you are getting more information from the MV_CAPABILITIES_TABLE.



Restriction 7 for basic MV fast refresh: It cannot contain nested queries that have ANY, ALL, or NOT EXISTS.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement.



Restriction 8 for basic MV fast refresh: It cannot contain a [START WITH à] CONNECT BY clause.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view.



Restriction 10 for basis MV fast refresh: ON COMMIT materialized views cannot have remote detail tables.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 2 for join MV fast refresh: Rowids of all the tables in the FROM list must appear in the SELECT list of the query.

Error message: ORA-12052: cannot fast refresh materialized view RWIJK.EMPDEPT_MV

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
the SELECT list does not have the rowids of all the detail tables

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view

Again, the ORA-12052 error message leads to a much clearer message in the capabilities table.



Restriction 4 for join MV fast refresh:
Materialized view logs must exist with rowids for all the base tables in the FROM list of the query.

Error message: ORA-23413: table "RWIJK"."MYDEPT" does not have a materialized view log

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
the detail table does not have a materialized view log

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view

The error message was already very clear, and the contents of the capabilities table is so as well.



Restriction 5 for join MV fast refresh: You cannot create a fast refreshable materialized view from multiple tables with simple joins that include an object type column in the SELECT statement.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 6 for join MV fast refresh: ANSI joins are not possible

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 2 for aggregate MV fast refresh:All tables in the materialized view must have materialized view logs

Error message: ORA-23413: table "RWIJK"."MYEMP" does not have a materialized view log

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
the detail table does not have a materialized view log

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view



Restriction 3 for aggregate MV fast refresh:The materialized view logs must contain all columns from the table referenced in the materialized view

Error message: ORA-12033: cannot use filter columns from materialized view log on
"RWIJK"."MYEMP"

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
mv log does not have all necessary columns

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view



Restriction 4 for aggregate MV fast refresh:None of the columns in the base table, referred to in the materialized view log, can be encrypted.

Error message: ORA-32412: encrypted column "DEPTNO" not allowed in the materialized view log

MV capabilities: because the MV log could not even be created, it's not possible to collect a message from the MV_CAPABILITIES_TABLE.



Restriction 5 for aggregate MV fast refresh:All tables in the materialized view must have materialized view logs specified with ROWID

Error message: ORA-12032: cannot use rowid column from materialized view log on
"RWIJK"."MYEMP"

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
mv log must have ROWID

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view



Restriction 6 for aggregate MV fast refresh:All tables in the materialized view must have materialized view logs specified with INCLUDING NEW VALUES.

Error message: ORA-32401: materialized view log on "RWIJK"."MYEMP" does not have new values

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
mv log must have new values

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view



Restriction 8 for aggregate MV fast refresh:Only SUM, COUNT, AVG, STDDEV, VARIANCE, MIN and MAX are supported for fast refresh.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 10 for aggregate MV fast refresh:Aggregate functions must occur only as the outermost part of the expression. That is, aggregates such as AVG(AVG(x)) or AVG(x)+ AVG(x) are not allowed.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 12 for aggregate MV fast refresh:If VARIANCE(expr) or STDDEV(expr) is specified, COUNT(expr) and SUM(expr) must be specified. Oracle recommends that SUM(expr *expr) be specified.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 13 for aggregate MV fast refresh:The SELECT list must contain all GROUP BY columns.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 15 for aggregate MV fast refresh:If the materialized view has SUM(expr) but no COUNT(expr), then fast refresh is supported only on conventional DML inserts and direct loads.

Error message: none; MV gets created but just doesn't fast refresh

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST Y


REFRESH_FAST_AFTER_INSERT Y


REFRESH_FAST_AFTER_ONETAB_DML N
SUM(expr) without COUNT(expr)

REFRESH_FAST_AFTER_ANY_DML N
mv log does not have sequence #

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view

This one is interesting and explains why the MV gets created, but doesn't (always) fast refresh: REFRESH_FAST and REFRESH_FAST_AFTER_INSERT are possible, but REFRESH_FAST_AFTER_ONETAB_DML is not possible. According to the documentation, the latter means:
If this capability is possible, fast refresh from a materialized view log is possible regardless of the type of update operation, provided all update operations are performed on a single table. If this capability is not possible, fast refresh from a materialized view log may not be possible when the update operations are performed on multiple tables.
If you look closely at my test of restriction 15 in the third post of this series, then you see I only perform one update on one table, so I am at a loss here as to why that single update doesn't cause the MV to get refreshed after commit. But at least the MV_CAPABILITIES_TABLE warns you by saying "SUM(expr) without COUNT(expr)".



Restriction 16 for aggregate MV fast refresh:If the materialized view has no COUNT(*), then fast refresh is supported only on conventional DML inserts and direct loads.

Error message: none; MV gets created but just doesn't fast refresh

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST Y


REFRESH_FAST_AFTER_INSERT Y


REFRESH_FAST_AFTER_ONETAB_DML N
COUNT(*) is not present in the select list

REFRESH_FAST_AFTER_ANY_DML N
mv log does not have sequence #

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view



Restriction 17 for aggregate MV fast refresh:A materialized view with MAX or MIN is fast refreshable after delete or mixed DML statements if it does not have a WHERE clause.

Error message: none; MV gets created but just doesn't fast refresh

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST Y


REFRESH_FAST_AFTER_INSERT Y


REFRESH_FAST_AFTER_ONETAB_DML N
mv uses the MIN or MAX aggregate functions

REFRESH_FAST_AFTER_ANY_DML N
mv log does not have sequence #

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view



Restriction 18 for aggregate MV fast refresh:For materialized views with CUBE, ROLLUP, grouping sets, or concatenation of them, the SELECT list should contain grouping distinguisher that can either be a GROUPING_ID function on all GROUP BY expressions or GROUPING functions one for each GROUP BY expression. For example, if the GROUP BY clause of the materialized view is "GROUP BY CUBE(a, b)", then the SELECT list should contain either "GROUPING_ID(a, b)" or "GROUPING(a) AND GROUPING(b)" for the materialized view to be fast refreshable.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 19 for aggregate MV fast refresh:For materialized views with CUBE, ROLLUP, grouping sets, or concatenation of them, GROUP BY should not result in any duplicate groupings. For example, "GROUP BY a, ROLLUP(a, b)" is not fast refreshable because it results in duplicate groupings "(a), (a, b), AND (a)".

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 21 for aggregate MV fast refresh:The SELECT column in the defining query cannot be a complex expression with columns from multiple base tables. A possible workaround to this is to use a nested materialized view.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 22 for aggregate MV fast refresh:Materialized aggregate views with outer joins are fast refreshable after conventional DML and direct loads, provided only the outer table has been modified.

Error message: none; MV gets created, but just doesn't fast refresh after modification on the inner table

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST Y


REFRESH_FAST_AFTER_INSERT Y


REFRESH_FAST_AFTER_ONETAB_DML Y


REFRESH_FAST_AFTER_ANY_DML N
mv log does not have sequence #

REFRESH_FAST_AFTER_ANY_DML N
mv log does not have sequence #

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view

And after adding "sequence" to the materialized view log, it says:

CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST Y


REFRESH_FAST_AFTER_INSERT Y


REFRESH_FAST_AFTER_ONETAB_DML Y


REFRESH_FAST_AFTER_ANY_DML Y


REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view

So no additional clue as to why the refresh doesn't happen: the MV_CAPABILITIES_TABLE indicates all is well.



Restriction 23 for aggregate MV fast refresh:Materialized aggregate views with outer joins are fast refreshable after conventional DML and direct loads, provided unique constraints exist on the join columns of the inner join table.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement

Note that in part three, the MV was created first, then the primary key was dropped. This lead to an MV that didn't refresh. Here I changed the order by first dropping the primary key and then trying to create the MV. This time it led to the above mentioned ORA-12054.



Restriction 24 for aggregate MV fast refresh:If there are outer joins, all the joins must be connected by ANDs and must use the equality (=) operator.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 25 for aggregate MV fast refresh:Materialized views with named views or subqueries in the FROM clause can be fast refreshed provided the views can be completely merged.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 26 for aggregate MV fast refresh:It cannot contain a HAVING clause with a subquery.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 1 for union all MV fast refresh:The defining query must have the UNION ALL operator at the top level.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 3 for union all MV fast refresh:The SELECT list of each query must include a UNION ALL marker, and the UNION ALL column must have a distinct constant numeric or string value in each UNION ALL branch. Further, the marker column must appear in the same ordinal position in the SELECT list of each query block.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 5 for union all MV fast refresh:Insert-only aggregate materialized view queries are not supported for materialized views with UNION ALL.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 6 for union all MV fast refresh:Remote tables are not supported for materialized views with UNION ALL.

Error message: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



Restriction 1 for nested MV fast refresh:All parent and base materialized views must contain joins or aggregates.

Error message: ORA-12053: this is not a valid nested materialized view

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
requirements not satisfied for fast refresh of nested mv

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view

"Requirements not satisfied for fast refresh of nested MV". Now that's not very helpful, is it? Exactly the same is said by the 'N' in the Possible column.



Restriction 2 for nested MV fast refresh: All the underlying objects (materialized views or tables) on which the materialized view is defined must have a materialized view log.

Error message: ORA-23413: table "RWIJK"."PARENT1_MV" does not have a materialized view log

MV capabilities:
CAPABILITY_NAME                P
------------------------------ -
MSGTXT
--------------------------------------------------------------------------------
REFRESH_FAST N


REFRESH_FAST_AFTER_INSERT N
the detail table does not have a materialized view log

REFRESH_FAST_AFTER_ONETAB_DML N
see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML N
see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

REFRESH_FAST_PCT N
PCT is not possible on any of the detail tables in the materialized view




Restriction 3 for nested MV fast refresh:You cannot create both a materialized view and a prebuilt materialized view on the same table.

Error message: ORA-32334: cannot create prebuilt materialized view on a table already referenced by a MV

MV capabilities: ORA-32342: The EXPLAIN_MVIEW facility failed to explain the materialized view statement



The above shows that situations exist in which the MV_CAPABILITIES_TABLE gives more precise information about what restriction you are hitting, and it shows much more information as well.

The next part will summarize all findings by starting with the error message and collecting all encountered error conditions. Hopefully that will be a very useful post for people developing fast refreshable views.

The script

Sunday, August 16, 2009

Fast refreshable materialized view errors, part five: nested MV's

Previous post about union all MV's

The restrictions regarding nested MV's are not in the fast-refreshable part of the documentation, but a little earlier: here.

I noticed three restrictions in there:

1) All parent and base materialized views must contain joins or aggregates.
2) All the underlying objects (materialized views or tables) on which the materialized view is defined must have a materialized view log.
3) You cannot create both a materialized view and a prebuilt materialized view on the same table.
Let's inspect the related error messages, but I'll start again with a situation where everything works. Two parent materialized views and on top of them one nested materialized view:
rwijk@ORA11GR1> create materialized view log on emp
2 with primary key, rowid (deptno,sal) including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view log on dept with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view parent1_mv
2 refresh fast on commit
3 as
4 select emp.rowid emp_rowid
5 , dept.rowid dept_rowid
6 , emp.ename
7 , emp.deptno
8 , emp.sal
9 , dept.dname
10 from emp
11 , dept
12 where emp.deptno = dept.deptno
13 /

Materialized view created.

rwijk@ORA11GR1> create materialized view parent2_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sum(sal) sumsal
6 , count(sal) countsal
7 , count(*) countall
8 from emp
9 group by deptno
10 /

Materialized view created.

rwijk@ORA11GR1> create materialized view log on parent1_mv with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view log on parent2_mv with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view nested_mv
2 refresh fast on commit
3 as
4 select p1.rowid p1_rowid
5 , p2.rowid p2_rowid
6 , p1.ename
7 , p1.sal
8 , p1.dname
9 , p2.sumsal
10 from parent1_mv p1
11 , parent2_mv p2
12 where p1.deptno = p2.deptno
13 /

Materialized view created.

Please note that the materialized view log on emp is already fully equipped for future uses. Parent1_mv is a join MV and parent2_mv is an aggregate MV. And the nested MV joins both. This works as can be seen by this little test where KING's salary is doubled to 10,000:
rwijk@ORA11GR1> select * from nested_mv
2 /

P1_ROWID P2_ROWID ENAME SAL DNAME SUMSAL
------------------ ------------------ ---------- ---------- -------------- ----------
AAAWnXAAEAAABRrAAA AAAWnZAAEAAABRzAAB SMITH 800 RESEARCH 10875
AAAWnXAAEAAABRrAAB AAAWnZAAEAAABRzAAA ALLEN 1600 SALES 9400
AAAWnXAAEAAABRrAAC AAAWnZAAEAAABRzAAA WARD 1250 SALES 9400
AAAWnXAAEAAABRrAAD AAAWnZAAEAAABRzAAB JONES 2975 RESEARCH 10875
AAAWnXAAEAAABRrAAE AAAWnZAAEAAABRzAAA MARTIN 1250 SALES 9400
AAAWnXAAEAAABRrAAF AAAWnZAAEAAABRzAAA BLAKE 2850 SALES 9400
AAAWnXAAEAAABRrAAG AAAWnZAAEAAABRzAAC CLARK 2450 ACCOUNTING 8750
AAAWnXAAEAAABRrAAH AAAWnZAAEAAABRzAAB SCOTT 3000 RESEARCH 10875
AAAWnXAAEAAABRrAAI AAAWnZAAEAAABRzAAC KING 5000 ACCOUNTING 8750
AAAWnXAAEAAABRrAAJ AAAWnZAAEAAABRzAAA TURNER 1500 SALES 9400
AAAWnXAAEAAABRrAAK AAAWnZAAEAAABRzAAB ADAMS 1100 RESEARCH 10875
AAAWnXAAEAAABRrAAL AAAWnZAAEAAABRzAAA JAMES 950 SALES 9400
AAAWnXAAEAAABRrAAM AAAWnZAAEAAABRzAAB FORD 3000 RESEARCH 10875
AAAWnXAAEAAABRrAAN AAAWnZAAEAAABRzAAC MILLER 1300 ACCOUNTING 8750

14 rows selected.

rwijk@ORA11GR1> update emp set sal = 10000 where ename = 'KING'
2 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from nested_mv
2 /

P1_ROWID P2_ROWID ENAME SAL DNAME SUMSAL
------------------ ------------------ ---------- ---------- -------------- ----------
AAAWnXAAEAAABRrAAA AAAWnZAAEAAABRzAAB SMITH 800 RESEARCH 10875
AAAWnXAAEAAABRrAAB AAAWnZAAEAAABRzAAA ALLEN 1600 SALES 9400
AAAWnXAAEAAABRrAAC AAAWnZAAEAAABRzAAA WARD 1250 SALES 9400
AAAWnXAAEAAABRrAAD AAAWnZAAEAAABRzAAB JONES 2975 RESEARCH 10875
AAAWnXAAEAAABRrAAE AAAWnZAAEAAABRzAAA MARTIN 1250 SALES 9400
AAAWnXAAEAAABRrAAF AAAWnZAAEAAABRzAAA BLAKE 2850 SALES 9400
AAAWnXAAEAAABRrAAH AAAWnZAAEAAABRzAAB SCOTT 3000 RESEARCH 10875
AAAWnXAAEAAABRrAAJ AAAWnZAAEAAABRzAAA TURNER 1500 SALES 9400
AAAWnXAAEAAABRrAAK AAAWnZAAEAAABRzAAB ADAMS 1100 RESEARCH 10875
AAAWnXAAEAAABRrAAL AAAWnZAAEAAABRzAAA JAMES 950 SALES 9400
AAAWnXAAEAAABRrAAM AAAWnZAAEAAABRzAAB FORD 3000 RESEARCH 10875
AAAWnXAAEAAABRrAAG AAAWnZAAEAAABRzAAC CLARK 2450 ACCOUNTING 13750
AAAWnXAAEAAABRrAAN AAAWnZAAEAAABRzAAC MILLER 1300 ACCOUNTING 13750
AAAWnXAAEAAABRtAAA AAAWnZAAEAAABRzAAC KING 10000 ACCOUNTING 13750

14 rows selected.


1) All parent and base materialized views must contain joins or aggregates.

Let's replace the join MV, parent1_mv, by a basic MV. Note that I'll have to recreate the other MV's as well to avoid the "ORA-12034: materialized view log younger than last refresh" message. In real life with large tables you don't want to recreate everything from scratch when doing a little maintenance. In that case, use prebuilt tables so you can split the MV in a data part (the table) and a definition part (the MV). You can then recover from an ORA-12034 by just replacing the definition part. This is a useful link regarding this subject. Although it looks like the scripts at the end have not been tested, the ideas are sound. But I digress.

rwijk@ORA11GR1> update emp set sal = 5000 where ename = 'KING'
2 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view parent1_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> drop materialized view parent2_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> drop materialized view nested_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view parent1_mv
2 refresh fast on commit
3 as
4 select empno
5 , ename
6 , deptno
7 , sal
8 from emp
9 /

Materialized view created.

rwijk@ORA11GR1> create materialized view parent2_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sum(sal) sumsal
6 , count(sal) countsal
7 , count(*) countall
8 from emp
9 group by deptno
10 /

Materialized view created.

rwijk@ORA11GR1> create materialized view log on parent1_mv with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view log on parent2_mv with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view nested_mv
2 refresh fast on commit
3 as
4 select p1.rowid p1_rowid
5 , p2.rowid p2_rowid
6 , p1.ename
7 , p1.sal
8 , p2.sumsal
9 from parent1_mv p1
10 , parent2_mv p2
11 where p1.deptno = p2.deptno
12 /
from parent1_mv p1
*
ERROR at line 9:
ORA-12053: this is not a valid nested materialized view

I'm not exactly fond of a "is not valid" error message, as it can be paraphrased more meaningful, but we've seen worse in this series.

Let's try the other possibility: a union all MV.

rwijk@ORA11GR1> drop materialized view parent1_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> drop materialized view parent2_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view parent1_mv
2 refresh fast on commit
3 as
4 select emp.rowid emp_rowid
5 , empno
6 , ename
7 , deptno
8 , sal
9 , 'E1' umarker
10 from emp
11 where deptno = 10
12 union all
13 select emp.rowid emp_rowid
14 , empno
15 , ename
16 , deptno
17 , sal
18 , 'E2' umarker
19 from emp
20 where deptno = 20
21 /

Materialized view created.

rwijk@ORA11GR1> create materialized view parent2_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sum(sal) sumsal
6 , count(sal) countsal
7 , count(*) countall
8 from emp
9 group by deptno
10 /

Materialized view created.

rwijk@ORA11GR1> create materialized view log on parent1_mv with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view log on parent2_mv with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view nested_mv
2 refresh fast on commit
3 as
4 select p1.rowid p1_rowid
5 , p2.rowid p2_rowid
6 , p1.ename
7 , p1.sal
8 , p2.sumsal
9 from parent1_mv p1
10 , parent2_mv p2
11 where p1.deptno = p2.deptno
12 /

Materialized view created.

rwijk@ORA11GR1>
rwijk@ORA11GR1> select * from parent1_mv
2 /

EMP_ROWID EMPNO ENAME DEPTNO SAL UM
------------------ ---------- ---------- ---------- ---------- --
AAARhlAAEAAAAI3AAG 7782 CLARK 10 2450 E1
AAARhlAAEAAAAI3AAI 7839 KING 10 5000 E1
AAARhlAAEAAAAI3AAN 7934 MILLER 10 1300 E1
AAARhlAAEAAAAI3AAA 7369 SMITH 20 800 E2
AAARhlAAEAAAAI3AAD 7566 JONES 20 2975 E2
AAARhlAAEAAAAI3AAH 7788 SCOTT 20 3000 E2
AAARhlAAEAAAAI3AAK 7876 ADAMS 20 1100 E2
AAARhlAAEAAAAI3AAM 7902 FORD 20 3000 E2

8 rows selected.

rwijk@ORA11GR1> select * from nested_mv
2 /

P1_ROWID P2_ROWID ENAME SAL SUMSAL
------------------ ------------------ ---------- ---------- ----------
AAAWnoAAEAAABRrAAA AAAWnqAAEAAABRzAAC CLARK 2450 8750
AAAWnoAAEAAABRrAAB AAAWnqAAEAAABRzAAC KING 5000 8750
AAAWnoAAEAAABRrAAC AAAWnqAAEAAABRzAAC MILLER 1300 8750
AAAWnoAAEAAABRrAAD AAAWnqAAEAAABRzAAB SMITH 800 10875
AAAWnoAAEAAABRrAAE AAAWnqAAEAAABRzAAB JONES 2975 10875
AAAWnoAAEAAABRrAAF AAAWnqAAEAAABRzAAB SCOTT 3000 10875
AAAWnoAAEAAABRrAAG AAAWnqAAEAAABRzAAB ADAMS 1100 10875
AAAWnoAAEAAABRrAAH AAAWnqAAEAAABRzAAB FORD 3000 10875

8 rows selected.

rwijk@ORA11GR1> update emp set sal = 10000 where ename = 'KING'
2 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from parent1_mv
2 /

EMP_ROWID EMPNO ENAME DEPTNO SAL UM
------------------ ---------- ---------- ---------- ---------- --
AAARhlAAEAAAAI3AAG 7782 CLARK 10 2450 E1
AAARhlAAEAAAAI3AAN 7934 MILLER 10 1300 E1
AAARhlAAEAAAAI3AAA 7369 SMITH 20 800 E2
AAARhlAAEAAAAI3AAD 7566 JONES 20 2975 E2
AAARhlAAEAAAAI3AAH 7788 SCOTT 20 3000 E2
AAARhlAAEAAAAI3AAK 7876 ADAMS 20 1100 E2
AAARhlAAEAAAAI3AAM 7902 FORD 20 3000 E2
AAARhlAAEAAAAI3AAI 7839 KING 10 10000 E1

8 rows selected.

rwijk@ORA11GR1> select * from nested_mv
2 /

P1_ROWID P2_ROWID ENAME SAL SUMSAL
------------------ ------------------ ---------- ---------- ----------
AAAWnoAAEAAABRrAAD AAAWnqAAEAAABRzAAB SMITH 800 10875
AAAWnoAAEAAABRrAAE AAAWnqAAEAAABRzAAB JONES 2975 10875
AAAWnoAAEAAABRrAAF AAAWnqAAEAAABRzAAB SCOTT 3000 10875
AAAWnoAAEAAABRrAAG AAAWnqAAEAAABRzAAB ADAMS 1100 10875
AAAWnoAAEAAABRrAAH AAAWnqAAEAAABRzAAB FORD 3000 10875
AAAWnoAAEAAABRrAAA AAAWnqAAEAAABRzAAC CLARK 2450 13750
AAAWnoAAEAAABRrAAC AAAWnqAAEAAABRzAAC MILLER 1300 13750
AAAWnoAAEAAABRtAAA AAAWnqAAEAAABRzAAC KING 10000 13750

8 rows selected.

This scenario works. And parent1_mv is clearly neither a join MV nor an aggregate MV. So it looks like the Oracle documentation needs an update here.

When an underlying MV is more complex (join, aggregate, union all), then everything works, but when the underlying MV is simple, it breaks. Makes me wonder why. Does anyone have a clue? Or is it just Oracle saying to the developer that he made his MV design too complex, and he can do with one MV less?


2) All the underlying objects (materialized views or tables) on which the materialized view is defined must have a materialized view log.

A rather obvious one, but for the record:

rwijk@ORA11GR1> update emp set sal = 5000 where ename = 'KING'
2 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view log on parent1_mv
2 /

Materialized view log dropped.

rwijk@ORA11GR1> update emp set sal = 10000 where ename = 'KING'
2 /

1 row updated.

rwijk@ORA11GR1> commit
2 /
commit
*
ERROR at line 1:
ORA-23413: table "RWIJK"."PARENT1_MV" does not have a materialized view log
And yes I know, if dropping the materialized view log would have worked this way, it would lead to an ORA-12034. But that's not the point here.


3) You cannot create both a materialized view and a prebuilt materialized view on the same table.

The documentation adds an example of what is meant here:
For example, If you have a table costs with a materialized view cost_mv based on it, you cannot then create a prebuilt materialized view on table costs. The result would make cost_mv a nested materialized view and this method of conversion is not supported.
I simulated this situation by removing the previous setup and introducing an auxiliary table myemp, acting as the costs table above.

rwijk@ORA11GR1> drop materialized view nested_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> drop materialized view parent2_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> drop materialized view parent1_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create table myemp as select * from emp
2 /

Table created.

rwijk@ORA11GR1> alter table myemp add primary key (empno)
2 /

Table altered.

rwijk@ORA11GR1> create materialized view log on myemp with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view parent1_mv
2 refresh fast on commit
3 as
4 select emp.rowid emp_rowid
5 , dept.rowid dept_rowid
6 , emp.ename
7 , emp.deptno
8 , emp.sal
9 , dept.dname
10 from myemp emp
11 , dept
12 where emp.deptno = dept.deptno
13 /

Materialized view created.

rwijk@ORA11GR1> create materialized view myemp
2 on prebuilt table
3 refresh fast on commit
4 as
5 select empno
6 , ename
7 , deptno
8 , sal
9 from emp
10 /
create materialized view myemp
*
ERROR at line 1:
ORA-32334: cannot create prebuilt materialized view on a table already referenced
by a MV
A nice clear error message.

In the next part I'll go through all previous situations again, and use the explain_mview procedure and mv_capabilities_table to see if this gives us more information than the error message itself.

Friday, July 31, 2009

Fast refreshable materialized view errors, part four: union all MV's

Previous post about aggregate MV's

The fourth part will discuss the restrictions and the accompanying error messages of union all MV's. There are only a few restrictions mentioned in the documentation here.

Again, I numbered them and edited them a little bit to contain only one restriction per item:

1) The defining query must have the UNION ALL operator at the top level.
2) Each query block in the UNION ALL query must satisfy the requirements of a fast refreshable materialized view with aggregates or a fast refreshable materialized view with joins.
3) The SELECT list of each query must include a UNION ALL marker, and the UNION ALL column must have a distinct constant numeric or string value in each UNION ALL branch. Further, the marker column must appear in the same ordinal position in the SELECT list of each query block.
4) Outer joins are not supported for materialized views with UNION ALL.
5) Insert-only aggregate materialized view queries are not supported for materialized views with UNION ALL.
6) Remote tables are not supported for materialized views with UNION ALL.
7) The compatibility initialization parameter must be set to 9.2.0 or higher to create a fast refreshable materialized view with UNION ALL.


First things first: let's start with a working union all MV.

rwijk@ORA11GR1> create materialized view log on dept with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view log on emp with rowid
2 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view empdept_mv
2 refresh fast on commit
3 as
4 select rowid rid
5 , empno nr
6 , ename name
7 , 'E' umarker
8 from emp
9 union all
10 select rowid
11 , deptno
12 , dname
13 , 'D'
14 from dept
15 /

Materialized view created.

rwijk@ORA11GR1> select * from empdept_mv
2 /

RID NR NAME U
------------------ ---------- -------------- -
AAARhlAAEAAAAI3AAA 7369 SMITH E
AAARhlAAEAAAAI3AAB 7499 ALLEN E
AAARhlAAEAAAAI3AAC 7521 WARD E
AAARhlAAEAAAAI3AAD 7566 JONES E
AAARhlAAEAAAAI3AAE 7654 MARTIN E
AAARhlAAEAAAAI3AAF 7698 BLAKE E
AAARhlAAEAAAAI3AAG 7782 CLARK E
AAARhlAAEAAAAI3AAH 7788 SCOTT E
AAARhlAAEAAAAI3AAI 7839 KING E
AAARhlAAEAAAAI3AAJ 7844 TURNER E
AAARhlAAEAAAAI3AAK 7876 ADAMS E
AAARhlAAEAAAAI3AAL 7900 JAMES E
AAARhlAAEAAAAI3AAM 7902 FORD E
AAARhlAAEAAAAI3AAN 7934 MILLER E
AAARhmAAEAAAAI/AAA 10 ACCOUNTING D
AAARhmAAEAAAAI/AAB 20 RESEARCH D
AAARhmAAEAAAAI/AAC 30 SALES D
AAARhmAAEAAAAI/AAD 40 OPERATIONS D

18 rows selected.

rwijk@ORA11GR1> insert into dept values (50,'IT','UTRECHT')
2 /

1 row created.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from empdept_mv
2 /

RID NR NAME U
------------------ ---------- -------------- -
AAARhlAAEAAAAI3AAA 7369 SMITH E
AAARhlAAEAAAAI3AAB 7499 ALLEN E
AAARhlAAEAAAAI3AAC 7521 WARD E
AAARhlAAEAAAAI3AAD 7566 JONES E
AAARhlAAEAAAAI3AAE 7654 MARTIN E
AAARhlAAEAAAAI3AAF 7698 BLAKE E
AAARhlAAEAAAAI3AAG 7782 CLARK E
AAARhlAAEAAAAI3AAH 7788 SCOTT E
AAARhlAAEAAAAI3AAI 7839 KING E
AAARhlAAEAAAAI3AAJ 7844 TURNER E
AAARhlAAEAAAAI3AAK 7876 ADAMS E
AAARhlAAEAAAAI3AAL 7900 JAMES E
AAARhlAAEAAAAI3AAM 7902 FORD E
AAARhlAAEAAAAI3AAN 7934 MILLER E
AAARhmAAEAAAAI/AAA 10 ACCOUNTING D
AAARhmAAEAAAAI/AAB 20 RESEARCH D
AAARhmAAEAAAAI/AAC 30 SALES D
AAARhmAAEAAAAI/AAD 40 OPERATIONS D
AAARhmAAEAAAAI+AAA 50 IT D

19 rows selected.


And now some variations on this theme per restriction.


1) The defining query must have the UNION ALL operator at the top level.

If the top level does nothing but selecting the columns of the inner query, then this restriction doesn't apply:

rwijk@ORA11GR1> drop materialized view empdept_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view empdept_mv
2 refresh fast on commit
3 as
4 select rid
5 , nr
6 , name
7 , umarker
8 from ( select rowid rid
9 , empno nr
10 , ename name
11 , 'E' umarker
12 from emp
13 union all
14 select rowid
15 , deptno
16 , dname
17 , 'D'
18 from dept
19 )
20 /

Materialized view created.

rwijk@ORA11GR1> insert into dept values (50,'IT','UTRECHT')
2 /

1 row created.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from empdept_mv
2 /

RID NR NAME U
------------------ ---------- -------------- -
AAARhlAAEAAAAI3AAA 7369 SMITH E
AAARhlAAEAAAAI3AAB 7499 ALLEN E
AAARhlAAEAAAAI3AAC 7521 WARD E
AAARhlAAEAAAAI3AAD 7566 JONES E
AAARhlAAEAAAAI3AAE 7654 MARTIN E
AAARhlAAEAAAAI3AAF 7698 BLAKE E
AAARhlAAEAAAAI3AAG 7782 CLARK E
AAARhlAAEAAAAI3AAH 7788 SCOTT E
AAARhlAAEAAAAI3AAI 7839 KING E
AAARhlAAEAAAAI3AAJ 7844 TURNER E
AAARhlAAEAAAAI3AAK 7876 ADAMS E
AAARhlAAEAAAAI3AAL 7900 JAMES E
AAARhlAAEAAAAI3AAM 7902 FORD E
AAARhlAAEAAAAI3AAN 7934 MILLER E
AAARhmAAEAAAAI/AAA 10 ACCOUNTING D
AAARhmAAEAAAAI/AAB 20 RESEARCH D
AAARhmAAEAAAAI/AAC 30 SALES D
AAARhmAAEAAAAI/AAD 40 OPERATIONS D
AAARhmAAEAAAAI+AAB 50 IT D

19 rows selected.


And it works. But if I add a where clause to the MV query, the restriction becomes apparent:

rwijk@ORA11GR1> delete dept where deptno = 50
2 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view empdept_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view empdept_mv
2 refresh fast on commit
3 as
4 select rid
5 , nr
6 , name
7 , umarker
8 from ( select rowid rid
9 , empno nr
10 , ename name
11 , 'E' umarker
12 from emp
13 union all
14 select rowid
15 , deptno
16 , dname
17 , 'D'
18 from dept
19 )
20 where nr > 0
21 /
where nr > 0
*
ERROR at line 20:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

The error message is ORA-12054. That's the one we've been seeing many times before.


2) Each query block in the UNION ALL query must satisfy the requirements of a fast refreshable materialized view with aggregates or a fast refreshable materialized view with joins.

This one is pretty obvious. If a query block doesn't fulfill all requirements, then wrapping it inside a union all MV certainly won't make it fast refreshable. And so by covering all other restrictions, this particular restriction is covered as well.


3) The SELECT list of each query must include a UNION ALL marker, and the UNION ALL column must have a distinct constant numeric or string value in each UNION ALL branch. Further, the marker column must appear in the same ordinal position in the SELECT list of each query block.

Let's try to create a union all MV without a union all marker:

rwijk@ORA11GR1> create materialized view empdept_mv
2 refresh fast on commit
3 as
4 select rowid rid
5 , empno nr
6 , ename name
7 from emp
8 union all
9 select rowid
10 , deptno
11 , dname
12 from dept
13 /
from dept
*
ERROR at line 12:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view


4) Outer joins are not supported for materialized views with UNION ALL.

This one is not always true:

rwijk@ORA11GR1> create materialized view empdept_mv
2 refresh fast on commit
3 as
4 select emp.rowid rid
5 , dept.rowid rid2
6 , emp.deptno no
7 , dept.dname name
8 , 'E' umarker
9 from dept
10 , emp
11 where dept.deptno = emp.deptno(+)
12 union all
13 select rowid
14 , null
15 , deptno
16 , dname
17 , 'D'
18 from dept
19 /

Materialized view created.

rwijk@ORA11GR1> select * from empdept_mv
2 /

RID RID2 NO NAME U
------------------ ------------------ ---------- -------------- -
AAARhlAAEAAAAI3AAA AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAB AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAC AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAD AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAE AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAF AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAG AAARhmAAEAAAAI/AAA 10 ACCOUNTING E
AAARhlAAEAAAAI3AAH AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAI AAARhmAAEAAAAI/AAA 10 ACCOUNTING E
AAARhlAAEAAAAI3AAJ AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAK AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAL AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAM AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAN AAARhmAAEAAAAI/AAA 10 ACCOUNTING E
AAARhmAAEAAAAI/AAD OPERATIONS E
AAARhmAAEAAAAI/AAA 10 ACCOUNTING D
AAARhmAAEAAAAI/AAB 20 RESEARCH D
AAARhmAAEAAAAI/AAC 30 SALES D
AAARhmAAEAAAAI/AAD 40 OPERATIONS D

19 rows selected.

rwijk@ORA11GR1> insert into dept values (50,'IT','UTRECHT')
2 /

1 row created.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from empdept_mv
2 /

RID RID2 NO NAME U
------------------ ------------------ ---------- -------------- -
AAARhlAAEAAAAI3AAA AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAB AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAC AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAD AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAE AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAF AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAG AAARhmAAEAAAAI/AAA 10 ACCOUNTING E
AAARhlAAEAAAAI3AAH AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAI AAARhmAAEAAAAI/AAA 10 ACCOUNTING E
AAARhlAAEAAAAI3AAJ AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAK AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAL AAARhmAAEAAAAI/AAC 30 SALES E
AAARhlAAEAAAAI3AAM AAARhmAAEAAAAI/AAB 20 RESEARCH E
AAARhlAAEAAAAI3AAN AAARhmAAEAAAAI/AAA 10 ACCOUNTING E
AAARhmAAEAAAAI/AAD OPERATIONS E
AAARhmAAEAAAAI/AAA 10 ACCOUNTING D
AAARhmAAEAAAAI/AAB 20 RESEARCH D
AAARhmAAEAAAAI/AAC 30 SALES D
AAARhmAAEAAAAI/AAD 40 OPERATIONS D
AAARhmAAEAAAAI+AAB 50 IT D
AAARhmAAEAAAAI+AAB IT E

21 rows selected.


As shown, this union all MV with an outer join is fast refreshable here. I wouldn't rely on it, though.


5) Insert-only aggregate materialized view queries are not supported for materialized views with UNION ALL.

According to the documentation, an insert-only aggregate materialized view is a materialized view with one of the following:

* Materialized views with MIN or MAX aggregates
* Materialized views which have SUM(expr) but no COUNT(expr)
* Materialized views without COUNT(*)

And it is indeed not allowed:

rwijk@ORA11GR1> delete dept where deptno = 50
2 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view empdept_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view empdept_mv
2 refresh fast on commit
3 as
4 select max(rowid) rid
5 , max(empno) no
6 , max(ename) name
7 , 'E' umarker
8 from emp
9 union all
10 select rowid
11 , deptno
12 , dname
13 , 'D'
14 from dept
15 /
, 'D'
*
ERROR at line 13:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view


6) Remote tables are not supported for materialized views with UNION ALL.

rwijk@ORA11GR1> create database link ora10
2 connect to rwijk
3 identified by rwijk
4 using 'ora10gr2'
5 /

Database link created.

rwijk@ORA11GR1> create materialized view empdept_mv
2 refresh fast on commit
3 as
4 select emp.rowid rid
5 , emp.empno no
6 , emp.ename name
7 , 'E' umarker
8 from emp@ora10
9 union all
10 select rowid
11 , deptno
12 , dname
13 , 'D'
14 from dept
15 /
from dept
*
ERROR at line 14:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view


7) The compatibility initialization parameter must be set to 9.2.0 or higher to create a fast refreshable materialized view with UNION ALL.

According to the documentation here, I can only set the COMPATIBLE parameter to a value below 9.2.0 when the database itself is a 9iR2 database. I don't have such a database here with me, so I'm unable to test this one. I'd be happy to hear the error message from someone with a 9iR2 database who can test this one.

Next up are nested MV's.

Friday, June 26, 2009

Fast refreshable materialized view errors, part three: aggregate MV's

Previous post about join MV's

In the third part I'm going to examine all restrictions for aggregate materialized views, as described in the documentation.

For convenience of this post, I have numbered and slightly rearranged them to contain one restriction per number:

1) All restrictions from "General Restrictions on Fast Refresh".
2) All tables in the materialized view must have materialized view logs
3) The materialized view logs must contain all columns from the table referenced in the materialized view.
4) None of the columns in the base table, referred to in the materialized view log, can be encrypted.
5) All tables in the materialized view must have materialized view logs specified with ROWID
6) All tables in the materialized view must have materialized view logs specified with INCLUDING NEW VALUES.
7) All tables in the materialized view must have materialized view logs, specified with the SEQUENCE clause if the table is expected to have a mix of inserts/direct-loads, deletes, and updates.
8) Only SUM, COUNT, AVG, STDDEV, VARIANCE, MIN and MAX are supported for fast refresh.
9) COUNT(*) must be specified.
10) Aggregate functions must occur only as the outermost part of the expression. That is, aggregates such as AVG(AVG(x)) or AVG(x)+ AVG(x) are not allowed.
11) For each aggregate such as AVG(expr), the corresponding COUNT(expr) must be present. Oracle recommends that SUM(expr) be specified.
12) If VARIANCE(expr) or STDDEV(expr) is specified, COUNT(expr) and SUM(expr) must be specified. Oracle recommends that SUM(expr *expr) be specified.
13) The SELECT list must contain all GROUP BY columns.
14) If the materialized view has MIN or MAX aggregates, then fast refresh is supported only on conventional DML inserts and direct loads.
15) If the materialized view has SUM(expr) but no COUNT(expr), then fast refresh is supported only on conventional DML inserts and direct loads.
16) If the materialized view has no COUNT(*), then fast refresh is supported only on conventional DML inserts and direct loads.
17) A materialized view with MAX or MIN is fast refreshable after delete or mixed DML statements if it does not have a WHERE clause.
18) Materialized views with named views or subqueries in the FROM clause can be fast refreshed provided the views can be completely merged.
19) For materialized views with CUBE, ROLLUP, grouping sets, or concatenation of them, the SELECT list should contain grouping distinguisher that can either be a GROUPING_ID function on all GROUP BY expressions or GROUPING functions one for each GROUP BY expression. For example, if the GROUP BY clause of the materialized view is "GROUP BY CUBE(a, b)", then the SELECT list should contain either "GROUPING_ID(a, b)" or "GROUPING(a) AND GROUPING(b)" for the materialized view to be fast refreshable.
20) For materialized views with CUBE, ROLLUP, grouping sets, or concatenation of them, GROUP BY should not result in any duplicate groupings. For example, "GROUP BY a, ROLLUP(a, b)" is not fast refreshable because it results in duplicate groupings "(a), (a, b), AND (a)".
21) If there are no outer joins, you may have arbitrary selections and joins in the WHERE clause.
22) The SELECT column in the defining query cannot be a complex expression with columns from multiple base tables. A possible workaround to this is to use a nested materialized view.
23) Materialized aggregate views with outer joins are fast refreshable after conventional DML and direct loads, provided only the outer table has been modified.
24) Materialized aggregate views with outer joins are fast refreshable after conventional DML and direct loads, provided unique constraints exist on the join columns of the inner join table.
25) If there are outer joins, all the joins must be connected by ANDs and must use the equality (=) operator.
As mentioned in the first blogpost in this series, there is an extra restriction regarding aggregate MV's:
26) It cannot contain a HAVING clause with a subquery.

So this will be quite a lengthy and even tedious post, as you can imagine by the list above ... but for a good cause :-)

First, let's start with a base situation containing an aggregate MV:

rwijk@ORA11GR1> create table myemp as select * from emp
2 /

Table created.

rwijk@ORA11GR1> alter table myemp add primary key (empno)
2 /

Table altered.

rwijk@ORA11GR1> create materialized view log on myemp
2 with rowid (deptno) including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , count(*) cnt
6 from myemp
7 group by deptno
8 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO CNT
---------- ----------
30 6
20 5
10 3

3 rows selected.

And to prove that the MV is fast refreshed on commit:

rwijk@ORA11GR1> insert into myemp (empno,ename,job,sal,deptno)
2 values (7777,'VAN WIJK','JANITOR',500,10)
3 /

1 row created.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO CNT
---------- ----------
30 6
20 5
10 4

3 rows selected.


1) All restrictions from "General Restrictions on Fast Refresh".

These have been examined in part one.


2) All tables in the materialized view must have materialized view logs

This one is quite obvious, but I'm still executing it anyway to collect the error message.

rwijk@ORA11GR1> delete myemp where empno = 7777
2 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> drop materialized view log on myemp
2 /

Materialized view log dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , count(*) cnt
6 from myemp
7 group by deptno
8 /
from myemp
*
ERROR at line 6:
ORA-23413: table "RWIJK"."MYEMP" does not have a materialized view log

The error message is clear as to what you should be doing to resolve this situation.


3) The materialized view logs must contain all columns from the table referenced in the materialized view.

To test this one, I create the materialized view log without the deptno column:

rwijk@ORA11GR1> create materialized view log on myemp
2 with rowid including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , count(*) cnt
6 from myemp
7 group by deptno
8 /
from myemp
*
ERROR at line 6:
ORA-12033: cannot use filter columns from materialized view log on
"RWIJK"."MYEMP"

And the materialized view does not get created, and with quite a clear error message, although mentioning a missing column would be convenient.


4) None of the columns in the base table, referred to in the materialized view log, can be encrypted.

I never did encryption of columns before, but it's actually quite easy to setup. First I add this line to my sqlnet.ora to specify the location of my wallet file:

ENCRYPTION_WALLET_LOCATION=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=C:\oracle\product\11.1.0\mydb2\NETWORK\ADMIN\)))

Then I set the encryption key once. For subsequent runs it is not needed to set anymore, so I commented it out. The step creates a file called "ewallet.p12" in the location specified in the previous step.

rwijk@ORA11GR1> --alter system set encryption key identified by rwijk
rwijk@ORA11GR1> --/

Next action is to open the encryption wallet and encrypt the deptno column:

rwijk@ORA11GR1> alter system set encryption wallet open identified by rwijk
2 /

System altered.

rwijk@ORA11GR1> alter table myemp modify (deptno encrypt)
2 /

Table altered.

Now everything is ready to do the test:

rwijk@ORA11GR1> drop materialized view log on myemp
2 /

Materialized view log dropped.

rwijk@ORA11GR1> create materialized view log on myemp
2 with rowid (deptno) including new values
3 /
create materialized view log on myemp
*
ERROR at line 1:
ORA-32412: encrypted column "DEPTNO" not allowed in the materialized view log


rwijk@ORA11GR1> alter system set encryption wallet close
2 /

System altered.


The error message cannot be more clear.


5) All tables in the materialized view must have materialized view logs specified with ROWID.

rwijk@ORA11GR1> drop table myemp purge
2 /

Table dropped.

rwijk@ORA11GR1> create table myemp as select * from emp
2 /

Table created.

rwijk@ORA11GR1> alter table myemp add primary key (empno)
2 /

Table altered.

rwijk@ORA11GR1> create materialized view log on myemp
2 with sequence (deptno) including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , count(*) cnt
6 from myemp
7 group by deptno
8 /
from myemp
*
ERROR at line 6:
ORA-12032: cannot use rowid column from materialized view log on
"RWIJK"."MYEMP"

Clear.


6) All tables in the materialized view must have materialized view logs specified with INCLUDING NEW VALUES.

rwijk@ORA11GR1> drop materialized view log on myemp
2 /

Materialized view log dropped.

rwijk@ORA11GR1> create materialized view log on myemp
2 with rowid (deptno)
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , count(*) cnt
6 from myemp
7 group by deptno
8 /
from myemp
*
ERROR at line 6:
ORA-32401: materialized view log on "RWIJK"."MYEMP" does not have new values


Again clear.


7) All tables in the materialized view must have materialized view logs, specified with the SEQUENCE clause if the table is expected to have a mix of inserts/direct-loads, deletes, and updates.

I cannot reproduce this one, although I must admit that I did try, but not too hard. For a simple case it just works:

rwijk@ORA11GR1> drop materialized view log on myemp
2 /

Materialized view log dropped.

rwijk@ORA11GR1> create materialized view log on myemp
2 with rowid (deptno) including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , count(*) cnt
6 from myemp
7 group by deptno
8 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO CNT
---------- ----------
30 6
20 5
10 3

3 rows selected.

rwijk@ORA11GR1> insert into myemp (empno,ename,job,sal,deptno)
2 values (7777,'VAN WIJK','JANITOR',500,10)
3 /

1 row created.

rwijk@ORA11GR1> delete myemp
2 where empno = 7777
3 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO CNT
---------- ----------
30 6
20 5
10 3

3 rows selected.

But you'd better specify the sequence clause in production code, after you've been warned by the documentation.


8) Only SUM, COUNT, AVG, STDDEV, VARIANCE, MIN and MAX are supported for fast refresh.

Let's try another aggregate function: the MAX...KEEP...FIRST/LAST one.

rwijk@ORA11GR1> drop table myemp purge
2 /

Table dropped.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create table myemp as select * from emp
2 /

Table created.

rwijk@ORA11GR1> alter table myemp add primary key (empno)
2 /

Table altered.

rwijk@ORA11GR1> create materialized view log on myemp
2 with rowid (deptno,sal,hiredate) including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , max(sal) keep (dense_rank first order by hiredate) sal_first_hired_emp
6 , count(*) cnt
7 from myemp
8 group by deptno
9 /
from myemp
*
ERROR at line 7:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

We've seen the ORA-12054 before...


9) COUNT(*) must be specified.

This one doesn't reproduce for simple examples:

rwijk@ORA11GR1> drop materialized view log on myemp
2 /

Materialized view log dropped.

rwijk@ORA11GR1> create materialized view log on myemp
2 with rowid (deptno,sal) including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sum(sal) sumsal
6 from myemp
7 group by deptno
8 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO SUMSAL
---------- ----------
30 9400
20 10875
10 8750

3 rows selected.

rwijk@ORA11GR1> insert into myemp (empno,ename,job,sal,deptno)
2 values (7777,'VAN WIJK','JANITOR',500,10)
3 /

1 row created.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO SUMSAL
---------- ----------
30 9400
20 10875
10 9250

3 rows selected.

It simply works, but again: I'd still specify count(*) in production code.


10) Aggregate functions must occur only as the outermost part of the expression. That is, aggregates such as AVG(AVG(x)) or AVG(x)+ AVG(x) are not allowed.

rwijk@ORA11GR1> delete myemp where empno = 7777
2 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , avg(sal) + avg(sal) twice_avg_sal
6 , count(*) cnt
7 from myemp
8 group by deptno
9 /
from myemp
*
ERROR at line 7:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

Again, an ORA-12054.


11) For each aggregate such as AVG(expr), the corresponding COUNT(expr) must be present. Oracle recommends that SUM(expr) be specified.

For the below materialized view, COUNT(sal) must be present according to this restriction, but:

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sum(sal) sumsal
6 , count(*) cnt
7 from myemp
8 group by deptno
9 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO SUMSAL CNT
---------- ---------- ----------
30 9400 6
20 10875 5
10 8750 3

3 rows selected.

rwijk@ORA11GR1> insert into myemp (empno,ename,job,sal,deptno)
2 values (7777,'VAN WIJK','JANITOR',500,10)
3 /

1 row created.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO SUMSAL CNT
---------- ---------- ----------
30 9400 6
20 10875 5
10 9250 4

3 rows selected.

Again it just works and again I'd still add the COUNT(expr) anyway.


12) If VARIANCE(expr) or STDDEV(expr) is specified, COUNT(expr) and SUM(expr) must be specified. Oracle recommends that SUM(expr *expr) be specified.

An example where I don't specify the SUM(sal):

rwijk@ORA11GR1> delete myemp where empno = 7777
2 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , variance(sal) varsal
6 , count(sal) cntsal
7 , count(*) cnt
8 from myemp
9 group by deptno
10 /
from myemp
*
ERROR at line 8:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

And again the multifunctional ORA-12054.


13) The SELECT list must contain all GROUP BY columns.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select count(*) cnt
5 from myemp
6 group by deptno
7 /
from myemp
*
ERROR at line 5:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

Again, ORA-12054.


14) If the materialized view has MIN or MAX aggregates, then fast refresh is supported only on conventional DML inserts and direct loads.

Let's try an update, as that should be impossible with aggregate MV's containing a MIN or MAX:

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , max(sal) maxsal
6 , count(sal) cntsal
7 , count(*) cnt
8 from myemp
9 group by deptno
10 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO MAXSAL CNTSAL CNT
---------- ---------- ---------- ----------
30 2850 6 6
20 3000 5 5
10 5000 3 3

3 rows selected.

rwijk@ORA11GR1> update myemp
2 set sal = 0
3 where ename = 'KING'
4 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO MAXSAL CNTSAL CNT
---------- ---------- ---------- ----------
30 2850 6 6
20 3000 5 5
10 2450 3 3

3 rows selected.

And it works, but when you use it and it doesn't work, Oracle can say that you've been warned by the documentation :-)


15) If the materialized view has SUM(expr) but no COUNT(expr), then fast refresh is supported only on conventional DML inserts and direct loads.

This one contains SUM(sal), but no COUNT(sal):

rwijk@ORA11GR1> update myemp
2 set sal = 5000
3 where ename = 'KING'
4 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sum(sal) sumsal
6 , count(*) cnt
7 from myemp
8 group by deptno
9 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO SUMSAL CNT
---------- ---------- ----------
30 9400 6
20 10875 5
10 8750 3

3 rows selected.

rwijk@ORA11GR1> update myemp
2 set sal = 0
3 where ename = 'KING'
4 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO SUMSAL CNT
---------- ---------- ----------
30 9400 6
20 10875 5
10 8750 3

3 rows selected.

A nice example of a phenomenon I've experience several times myself: the materialized view is created successfully, but when testing the code, it simply doesn't refresh.


16) If the materialized view has no COUNT(*), then fast refresh is supported only on conventional DML inserts and direct loads.

The same phenomenon as in number 15:

rwijk@ORA11GR1> update myemp
2 set sal = 5000
3 where ename = 'KING'
4 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sum(sal) sumsal
6 , count(sal) cntsal
7 from myemp
8 group by deptno
9 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO SUMSAL CNTSAL
---------- ---------- ----------
30 9400 6
20 10875 5
10 8750 3

3 rows selected.

rwijk@ORA11GR1> update myemp
2 set sal = 0
3 where ename = 'KING'
4 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO SUMSAL CNTSAL
---------- ---------- ----------
30 9400 6
20 10875 5
10 8750 3

3 rows selected.

No error message and no refresh.


17) A materialized view with MAX or MIN is fast refreshable after delete or mixed DML statements if it does not have a WHERE clause.

Another example of no error message and no refresh:

rwijk@ORA11GR1> update myemp
2 set sal = 5000
3 where ename = 'KING'
4 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , max(sal) maxsal
6 , count(sal) cntsal
7 , count(*) cnt
8 from myemp
9 where sal > 1000
10 group by deptno
11 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO MAXSAL CNTSAL CNT
---------- ---------- ---------- ----------
30 2850 5 5
20 3000 4 4
10 5000 3 3

3 rows selected.

rwijk@ORA11GR1> delete myemp
2 where ename = 'KING'
3 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

DEPTNO MAXSAL CNTSAL CNT
---------- ---------- ---------- ----------
30 2850 5 5
20 3000 4 4
10 5000 3 3

3 rows selected.



18) For materialized views with CUBE, ROLLUP, grouping sets, or concatenation of them, the SELECT list should contain grouping distinguisher that can either be a GROUPING_ID function on all GROUP BY expressions or GROUPING functions one for each GROUP BY expression. For example, if the GROUP BY clause of the materialized view is "GROUP BY CUBE(a, b)", then the SELECT list should contain either "GROUPING_ID(a, b)" or "GROUPING(a) AND GROUPING(b)" for the materialized view to be fast refreshable.

Deliberately forgetting the GROUPING or GROUPING_ID functions:

rwijk@ORA11GR1> drop table myemp purge
2 /

Table dropped.

rwijk@ORA11GR1> create table myemp as select * from emp
2 /

Table created.

rwijk@ORA11GR1> alter table myemp add primary key (empno)
2 /

Table altered.

rwijk@ORA11GR1> create materialized view log on myemp
2 with rowid (deptno,sal) including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sal
6 , count(*) cnt
7 from myemp
8 group by cube(deptno,sal)
9 /
from myemp
*
ERROR at line 7:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view



19) For materialized views with CUBE, ROLLUP, grouping sets, or concatenation of them, GROUP BY should not result in any duplicate groupings. For example, "GROUP BY a, ROLLUP(a, b)" is not fast refreshable because it results in duplicate groupings "(a), (a, b), AND (a)".

Let's create a duplicate grouping set containing deptno.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , sal
6 , grouping_id(deptno,sal)
7 , count(*) cnt
8 from myemp
9 group by grouping sets(deptno,deptno,sal)
10 /
from myemp
*
ERROR at line 8:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view



20) If there are no outer joins, you may have arbitrary selections and joins in the WHERE clause.

I'm not sure how to test this one. It is not really a restriction, it just says that there is no limit on the number of joins and selected column if no outer join is used in the underlying query.


21) The SELECT column in the defining query cannot be a complex expression with columns from multiple base tables. A possible workaround to this is to use a nested materialized view.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select e.deptno + d.deptno
5 , count(*) cnt
6 from myemp e
7 , mydept d
8 where e.deptno = d.deptno
9 group by e.deptno
10 , d.deptno
11 /
from myemp e
*
ERROR at line 6:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view



22) Materialized aggregate views with outer joins are fast refreshable after conventional DML and direct loads, provided only the outer table has been modified.

First, let's remove department 10 and then create the MV with an outer join.

rwijk@ORA11GR1> delete mydept
2 where deptno = 10
3 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select e.deptno emp_deptno
5 , d.deptno dept_deptno
6 , count(*) cnt
7 from myemp e
8 , mydept d
9 where e.deptno = d.deptno (+)
10 group by e.deptno
11 , d.deptno
12 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
10 3
30 30 6
20 20 5

3 rows selected.


And now insert a record in the outer table.

rwijk@ORA11GR1> insert into myemp (empno,ename,job,sal,deptno)
2 values (7777,'VAN WIJK','JANITOR',500,10)
3 /

1 row created.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
10 4
30 30 6
20 20 5

3 rows selected.

This works. But a modification on the inner table should not work:

rwijk@ORA11GR1> update mydept
2 set deptno = 50
3 where deptno = 20
4 /

1 row updated.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
10 4
30 30 6
20 20 5

3 rows selected.

And indeed, it doesn't work. And we were not warned by an error message. And the view log is empty:

rwijk@ORA11GR1> select * from mlog$_mydept
2 /

no rows selected

And this is how the materialized view should look like if it was allowed:

rwijk@ORA11GR1> select e.deptno emp_deptno
2 , d.deptno dept_deptno
3 , count(*) cnt
4 from myemp e
5 , mydept d
6 where e.deptno = d.deptno (+)
7 group by e.deptno
8 , d.deptno
9 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
20 5
10 4
30 30 6

3 rows selected.



23) Materialized aggregate views with outer joins are fast refreshable after conventional DML and direct loads, provided unique constraints exist on the join columns of the inner join table.

Let's drop the primary key of mydept.

rwijk@ORA11GR1> alter table mydept drop constraint mydept_pk
2 /

Table altered.

rwijk@ORA11GR1> select * from emp_mv
2 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
10 4
30 30 6
20 20 5

3 rows selected.

rwijk@ORA11GR1> delete myemp
2 where empno = 7777
3 /

1 row deleted.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
10 4
30 30 6
20 20 5

3 rows selected.

No change in the materialized view. The change is still pending in the materialized view log:

rwijk@ORA11GR1> select * from mlog$_myemp
2 /

DEPTNO SAL
---------- ----------
M_ROW$$
--------------------------------------------------------------------------------
SNAPTIME$$ D O
------------------- - -
CHANGE_VECTOR$$
--------------------------------------------------------------------------------
10 500
AAATX0AAEAAAALWAAB
01-01-4000 00:00:00 D O
0000


1 row selected.

And the count for department 10 should now be 3, as can be seen by executing the query that's behind the materialized view:

rwijk@ORA11GR1> select e.deptno emp_deptno
2 , d.deptno dept_deptno
3 , count(*) cnt
4 from myemp e
5 , mydept d
6 where e.deptno = d.deptno (+)
7 group by e.deptno
8 , d.deptno
9 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
20 5
10 3
30 30 6

3 rows selected.


So no error message, but it just doesn't refresh.


24) If there are outer joins, all the joins must be connected by ANDs and must use the equality (=) operator.

First recreate the primary key on mydept and then change the where clause from "e.deptno = d.deptno (+)" to "e.deptno between d.deptno (+) and 100". Functionally the same, as all deptno's are less than 100.

rwijk@ORA11GR1> alter table mydept add constraint mydept_pk primary key (deptno)
2 /

Table altered.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select e.deptno emp_deptno
5 , d.deptno dept_deptno
6 , count(*) cnt
7 from myemp e
8 , mydept d
9 where e.deptno between d.deptno (+) and 100
10 group by e.deptno
11 , d.deptno
12 /
where e.deptno between d.deptno (+) and 100
*
ERROR at line 9:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view


25) Materialized views with named views or subqueries in the FROM clause can be fast refreshed provided the views can be completely merged.

Before showing the case where the view cannot be merged, first a case where the view can be merged:

rwijk@ORA11GR1> drop table mydept purge
2 /

Table dropped.

rwijk@ORA11GR1> create table mydept as select * from dept
2 /

Table created.

rwijk@ORA11GR1> alter table mydept add constraint mydept_pk primary key (deptno)
2 /

Table altered.

rwijk@ORA11GR1> create materialized view log on mydept
2 with rowid (deptno) including new values
3 /

Materialized view log created.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select e.deptno emp_deptno
5 , d.deptno dept_deptno
6 , count(*) cnt
7 from myemp e
8 , (select deptno from mydept) d
9 where e.deptno = d.deptno
10 group by e.deptno
11 , d.deptno
12 /

Materialized view created.

rwijk@ORA11GR1> select * from emp_mv
2 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
10 10 3
30 30 6
20 20 5

3 rows selected.

rwijk@ORA11GR1> insert into myemp (empno,ename,job,sal,deptno)
2 values (7777,'VAN WIJK','JANITOR',500,10)
3 /

1 row created.

rwijk@ORA11GR1> commit
2 /

Commit complete.

rwijk@ORA11GR1> select * from emp_mv
2 /

EMP_DEPTNO DEPT_DEPTNO CNT
---------- ----------- ----------
10 10 4
30 30 6
20 20 5

3 rows selected.


And now I add a dummy group by clause. It doesn't change the result of the query, but the view is not mergeable anymore.

rwijk@ORA11GR1> drop materialized view emp_mv
2 /

Materialized view dropped.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select e.deptno emp_deptno
5 , d.deptno dept_deptno
6 , count(*) cnt
7 from myemp e
8 , (select deptno from mydept group by deptno) d
9 where e.deptno = d.deptno
10 group by e.deptno
11 , d.deptno
12 /
from myemp e
*
ERROR at line 7:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view



26) It cannot contain a HAVING clause with a subquery.

rwijk@ORA11GR1> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno
5 , count(*) cnt
6 from myemp
7 group by deptno
8 having avg(sal) > (select 1000 from dual)
9 /
from myemp
*
ERROR at line 6:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view


And with number 26, all documented restrictions on aggregate MV's are examined. Next up are union all MV's.

Friday, June 5, 2009

OTN Forums versus Stack Overflow

A quick intermezzo between the MV error related posts:

For almost three years now, I am answering questions on a regular basis at the OTN Discussion Forums, mainly at the SQL and PL/SQL Forum. By reading answers and giving answers, you are not only helping others, but you'll also find you are learning at greater speed than before. Most of the regulars like the forums very much of course, despite the sometimes annoying forum software, and especially upgrades of this software.

A couple of weeks ago, I started answering some Oracle related questions on Stack Overflow. And I'm impressed. Very impressed. The guys from Fog Creek Software (you may know the company because of the famous Joel On Software blog, written by its CEO Joel Spolsky) really know what technical forum software should look like. Every little thing that has annoyed me about the OTN forum software, has been addressed by Stack Overflow. A couple of examples:

Duplicate threads
OTN has different forums for each technology. But at times the poster of a question is not entirely sure what the best place for his question is. So he posts the same question in different forums. Visitors of only one forum may end up answering questions that have already been answered in the other forum. Stack Overflow works with tags. A single question can have several tags. People who answer questions, search for questions with specific tags. A simple but great answer for the duplicate thread problem.

Posting code
OTN somehow has problems posting code correctly. The unequal sign <> disappears. An outer join sign (+) transforms to some small graphical icon. And most important: newbies cannot easily find how to post code, leading to many unreadable questions. Stack Overflow has a single button to transform your code in, well, code. Without strange transformations.

Point system
OTN has a point system where the poster of the question can reward answers by marking them as Correct or Helpful. But it's not mandatory to do so. And sometimes plain wrong answers get marked as correct, degrading the quality of the post for future google searchers. It also gives an unsatisfying feeling when some very time consuming or great answers do not get rewarded, while some simple one liners do get the full 10 points. And it bothers me to watch regulars go as far as begging for a point reward; this surely cannot be your life's goal. Stack Overflow works with reputation. The more reputation you have, the more privileges you receive. Everyone can up vote (+10) or down vote (-2) an answer or comment, after they have achieved some reputation themselves. A very convenient way to let the poster of the question know which answer is regarded highly by their peers. Your reputation will be damaged if you post rubbish and/or DKB-style commercial "Here are my notes" answers. And future searches will find much clearer and more valuable answers.

And there are several other topics as well at which Stack Overflow outperforms OTN. When using the site, you feel it has been given good thought, as you can also see in this video where Joel Spolsky explains the concepts.

However, there is one thing at which OTN still excels: the knowledge of the people and therefore the quality of the answers. And ultimately, that is what counts. Most regulars at OTN give great answers and a few are not. For Oracle related questions at Stack Overflow, it's the other way around now. It's still rather good, but not excellent. So, for the best learning experience, I still recommend OTN. But I wish that the best of both worlds can be combined somewhere in the future.

PS: if you want a good laugh, then you should read this hilarious Stack Overflow thread about the user with the most reputation.