Sie sind auf Seite 1von 64

Materialized Views

REFRESH FAST
Now that we know how materialized view logs track changes to base tables we can use them to perform fast materialized view refreshes, i.e. refreshes where only the individual materialized view rows affected by base table changes are updated. This is also called "incremental" refreshing. Earlier in this tutorial we saw how the rowids for each row in a materialized view changed after a complete refresh. Now let's see what happens to a materialized view's rowids after a fast refresh. First we use the REFRESH FAST clause to specify that the default refresh method should be fast.
create materialized view log on t with sequence ;

create materialized view mv REFRESH FAST as select * from t ;

select key, val, rowid from mv ;

KEY VAL

ROWID

---------- ----- -----------------1 a 2 b 3 c 4 AAAWm+AAEAAAAaMAAA AAAWm+AAEAAAAaMAAB AAAWm+AAEAAAAaMAAC AAAWm+AAEAAAAaMAAD

Now we refresh the materialized view. The "F" value for the "method" parameter ensures the refresh will be a Fast one.
execute dbms_mview.refresh( list => 'MV', method => 'F' );

select key, val, rowid from mv ;

KEY VAL

ROWID

---------- ----- -----------------1 a 2 b 3 c 4 AAAWm+AAEAAAAaMAAA AAAWm+AAEAAAAaMAAB AAAWm+AAEAAAAaMAAC AAAWm+AAEAAAAaMAAD

The rowids did not change. Thus, with a fast refresh the materialized view data is not touched when no changes have been made to the base table, unlike a complete refresh where each row would have been created anew. Now let's update a row in the base table.
update t set val = 'XX' where key = 3 ;

commit;

execute dbms_mview.refresh( list => 'MV', method => 'F' );

select key, val, rowid from mv ;

KEY VAL

ROWID

---------- ----- -----------------1 a 2 b 3 XX 4 AAAWm+AAEAAAAaMAAA AAAWm+AAEAAAAaMAAB AAAWm+AAEAAAAaMAAC AAAWm+AAEAAAAaMAAD

Still no change in the rowids. In row 3 we can see that VAL changed from "c" to "XX" though, telling us that row 3 was updated during the refresh. Defaults The REFRESH FAST clause of the CREATE MATERIALIZED VIEW command tells Oracle what type of refresh to perform when no refresh option is specified. A materialized view created with REFRESH FAST can still be refreshed completely if required though. In the following example note how, even though MV was created above with the REFRESH FAST clause, all its rowids change after the refresh. This indicates that a complete refresh was performed.
execute dbms_mview.refresh( list => 'MV', method => 'C' );

select key, val, rowid from mv ;

KEY VAL

ROWID

---------- ----- -----------------1 a 2 b 3 XX 4 AAAWm+AAEAAAAaMAAE AAAWm+AAEAAAAaMAAF AAAWm+AAEAAAAaMAAG AAAWm+AAEAAAAaMAAH

Similarly a materialized view created with REFRESH COMPLETE can be fast refreshed (assuming the materialized view is capable of being fast refreshed, we'll learn more about this later).
drop materialized view mv ;

create materialized view mv REFRESH COMPLETE as select * from t ;

select key, val, rowid from mv ;

KEY VAL

ROWID

---------- ----- -----------------1 a 2 b 3 XX AAAWnBAAEAAAAaMAAA AAAWnBAAEAAAAaMAAB AAAWnBAAEAAAAaMAAC

AAAWnBAAEAAAAaMAAD

execute dbms_mview.refresh( list => 'MV', method => 'F' );

select key, val, rowid from mv ;

KEY VAL

ROWID

---------- ----- -----------------1 a 2 b 3 XX 4 AAAWnBAAEAAAAaMAAA AAAWnBAAEAAAAaMAAB AAAWnBAAEAAAAaMAAC AAAWnBAAEAAAAaMAAD

Note how none of the rowids in MV changed, indicating a fast refresh. Cleanup
drop materialized view mv ;

drop materialized view log on t ;

update t set val = 'c' where key = 3 ; commit ;

Materialized Views

Purging Materialized View Logs


Oracle automatically purges rows in the materialized view log when they are no longer needed. In the example below note how the log table is empty after the refresh.
create materialized view log on t ;

create materialized view mv refresh fast as select * from t ;

select count(*) from mlog$_t ;

COUNT(*) ---------0

insert into t values ( 5, 'e' ) ; commit;

select count(*) from mlog$_t ;

COUNT(*) ---------1

execute dbms_mview.refresh( list => 'MV', method => 'F' );

select count(*) from mlog$_t ;

COUNT(*) ---------0

DBMS_MVEW.PURGE_LOG If a materialized view log needs to be purged manually for some reason a procedure called DBMS_MVEW.PURGE_LOG can be used.
select count(*) from mlog$_t ;

COUNT(*) ---------0

update t set val = 'X' where key = 5 ; commit;

select count(*) from mlog$_t ;

COUNT(*) ---------1

execute DBMS_MVIEW.PURGE_LOG( master => 'T', num => 9999, flag => 'delete' ) ;

select count(*) from mlog$_t ;

COUNT(*) ---------0

The "num" and "flag" parameters can be used to partially purge the log. See the PURGE_LOG manual page for further details. Once a materialized view log has been purged any materialized views dependent on the deleted rows cannot be fast refreshed. Attempting a fast refresh will raise an error.
execute dbms_mview.refresh( list => 'MV', method => 'F' ); BEGIN dbms_mview.refresh( list => 'MV', method => 'F' ); END;

* ERROR at line 1: ORA-12034: materialized view log on "SCOTT"."T" younger than last refresh ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2537 ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2743 ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2712 ORA-06512: at line 1

Such materialized views will need to be refreshed completely.


select * from mv ;

KEY VAL ---------- ----1 a 2 b 3 c 4 5 e

execute dbms_mview.refresh( list => 'MV', method => 'C' );

select * from mv ;

KEY VAL ---------- ----1 a 2 b 3 c 4 5 X

Cleanup
delete from t where key = 5 ; commit;

drop materialized view mv ;

drop materialized view log on t ;

Materialized Views
REFRESH FAST Categories
There are three ways to categorize a materialized view's ability to be fast refreshed. 1. It can never be fast refreshed. 2. It can always be fast refreshed. 3. It can be fast refreshed after certain kinds of changes to the base table but not others. For the first case Oracle will raise an error if you try to create such a materialized view with its refresh method defaulted to REFRESH FAST. In the example below table T does not have a materialized view log on it. Materialized views based on T cannot therefore be fast refreshed. If we attempt to create such a materialized view we get an error.
create materialized view MV REFRESH FAST as select * from t2 ; as select * from t2

* ERROR at line 3: ORA-23413: table "SCOTT"."T2" does not have a materialized view log

For the second case materialized views are created without error, obviously, and will always be fast refreshed unless a complete refresh is explicitly requested. The third case is a little trickier. The next example demonstrates why.
select * from t2 ;

KEY

T_KEY

AMT

---------- ---------- ---------10 20 30 40 50 1 1 1 2 2 100 300 200 250 150

create materialized view log on t2 with primary key, rowid, sequence ( t_key, amt ) including new values ;

create materialized view mv REFRESH FAST as select t_key, max( amt ) amt_max from t2 group by t_key ;

select rowid, t_key, amt_max from mv ;

ROWID

T_KEY

AMT_MAX

------------------ ---------- ---------AAAhMzAAEAAAEG8AAA AAAhMzAAEAAAEG8AAB 1 2 300 250

So far everything works as expected. We created a materialized view log and created a materialized view with fast refresh as its default refresh method. Let's try inserting a row into the base table.
insert into t2 values ( 5, 2, 500 ); commit;

execute dbms_mview.refresh( list => 'MV', method => 'F' );

select rowid, t_key, amt_max from mv ;

ROWID

T_KEY

AMT_MAX

------------------ ---------- ---------AAAhMzAAEAAAEG8AAA AAAhMzAAEAAAEG8AAB 1 2 300 500

Again, it worked as expected. The view was fast refreshed (the rowid's did not change after the DBMS_MVIEW.REFRESH command) and the materialized view correctly shows 500 as the maximum value for rows with T_KEY = 2. Now let's try deleting a row from the base table.
delete from t2 where key = 5 ; commit;

execute dbms_mview.refresh( list => 'MV', method => 'F' ); BEGIN dbms_mview.refresh( list => 'MV', method => 'F' ); END;

* ERROR at line 1: ORA-32314: REFRESH FAST of "SCOTT"."MV" unsupported after deletes/updates ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2255 ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2461 ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2430 ORA-06512: at line 1

This time we received an error when we attempted a fast refresh. The reason is because this type of materialized view is an "insert-only" materialized view, i.e. it is only fast refreshable for inserts and direct loads, not updates or deletes. (We will see why it is an insert-only view in the next topic, DBMS_MVIEW.EXPLAIN_MVIEW.) To synchronize an insert-only materialized view after a delete we need to do a complete refresh.
execute dbms_mview.refresh( list => 'MV', method => 'C' );

select rowid, t_key, amt_max from mv ;

ROWID

T_KEY

AMT_MAX

------------------ ---------- ---------AAAhMzAAEAAAEG8AAC AAAhMzAAEAAAEG8AAD 1 2 300 250

Restrictions on Fast Refresh So how do we know whether a materialized view can be fast refreshed each time, sometimes, or never? One way would be to learn all the documented restrictions for fast refreshable materialized views. Here are some of them. In general materialized views cannot be fast refreshed if the base tables do not have materialized view logs or the defining query:

contains an analytic function contains non-repeating expressions like SYSDATE or ROWNUM contains RAW or LONG RAW data types contains a subquery in the SELECT clause contains a MODEL clause contains a HAVING clause contains nested queries with ANY, ALL, or NOT EXISTS contains a CONNECT BY clause references remote tables in different databases

references remote tables in a single database and defaults to the ON COMMIT refresh mode references other materialized views which are not join or aggregate materialized views. There are even more restrictions for materialized views containing joins, aggregates, UNION ALL, subqueries, etc. They are documented in various sections of a few different manuals and are too numerous and complex to repeat here. The following links can help you find them if required though.

CREATE MATERIALIZED VIEW - FAST Clause General Restrictions on Fast Refresh Restrictions on Fast Refresh on Materialized Views with Joins Only Restrictions on Fast Refresh on Materialized Views with Aggregates Restrictions on Fast Refresh on Materialized Views with UNION ALL Restrictions for Materialized Views with Subqueries Restrictions for Materialized Views with Unions Containing Subqueries Restrictions for Using Multitier Materialized Views Restrictions for Materialized Views with Collection Columns Fortunately there is a second, simpler alternative for determining whether a materialized view is fast refreshable or not. It uses the DBMS_MVIEW.EXPLAIN_MVIEW utility which we will explore next.

Cleanup
drop materialized view mv ;

drop materialized view log on t2 ;

Materialized Views
DBMS_MVIEW.EXPLAIN_MVIEW
As we saw in the preceding topic, predicting whether or not a materialized view is fast refreshable can be complicated. TheDBMS_MVIEW.EXPLAIN_MVIEW utility can simplify this task however. Full details on how the utility works are available at the preceding link. The material below will help you use the utility effectively.

MV_CAPABILITIES_TABLE There are two ways to get the output from DBMS_MVIEW.EXPLAIN_MVIEW, via a table or via a varray. To use the table method the current schema must contain a table called MV_CAPABILITIES_TABLE. The full, documented CREATE TABLE command for MV_CAPABILITIES_TABLE can be found on UNIX systems at $ORACLE_HOME/rdbms/admin/utlxmv.sql. It is also available in Oracle's documentation at Oracle Database Data Warehousing Guide - Basic Materialized Views - Using MV_CAPABILITIES_TABLE (see Gotchafor a related bug). Here is an abridged version.
create table MV_CAPABILITIES_TABLE ( statement_id mvowner mvname capability_name possible related_text related_num msgno msgtxt seq ) ; varchar(30) , varchar(30) , varchar(30) , varchar(30) , character(1) , varchar(2000) , number , integer , varchar(2000) , number

VARRAY Output Using DBMS_MVIEW.EXPLAIN_MVIEW with the table output method typically involves

1. deleting old rows from MV_CAPABILITIES_TABLE 2. running DBMS_MVIEW.EXPLAIN_MVIEW 3. selecting new rows from MV_CAPABILITIES_TABLE. To save time in this tutorial we will use DBMS_MVIEW.EXPLAIN_MVIEW's varray output option instead and supplement it with a custom function called MY_MV_CAPABILITIES.
create or replace function my_mv_capabilities ( p_mv p_capability_name_filter in in varchar2 , varchar2 default '%' , varchar2 default 'N' , number default 80

p_include_pct_capabilities in p_linesize ) return clob as in

--------------------------------------------------------------------------------- From http://www.sqlsnippets.com/en/topic-12884.html --- Parameters: --------p_capability_name_filter o use either REFRESH, REWRITE, PCT, or the default p_mv o this value is passed to DBMS_MVIEW.EXPLAIN_MVIEW's "mv" parameter o it can contain either a query, CREATE MATERIALIZED VIEW command text, or a materialized view name

----------- Typical Usage: --------------------------------------------------------------------------------------o the value 5000 is arbitraty; any value big enough to contain the report output will do set long 5000 select my_mv_capabilities( 'MV_NAME' ) as mv_report from dual ; p_linesize o the maximum size allowed for any line in the report output o data that is longer than this value will be word wrapped p_include_pct_capabilities Y - capabilities like REFRESH_FAST_PCT are included in the report N - capabilities like REFRESH_FAST_PCT are not included in the report

pragma autonomous_transaction ;

v_nl constant char(1) := unistr( '\000A' ); -- new line

v_previous_possible char(1) := 'X' ;

v_capabilities sys.ExplainMVArrayType ;

v_output clob ;

begin

dbms_mview.explain_mview( mv => p_mv, msg_array => v_capabilities ) ;

for v_capability in ( select capability_name , possible , related_text , msgtxt from table( v_capabilities ) where capability_name like '%' || upper( p_capability_name_filter ) || '%' and not ( capability_name like '%PCT%' and upper(p_include_pct_capabilities) = 'N' ) order by mvowner ,

mvname , possible desc , seq ) loop

------------------------------------------------------------- print section heading ------------------------------------------------------------

if v_capability.possible <> v_previous_possible then

v_output := v_output || v_nl || case v_capability.possible when 'T' then 'Capable of: ' when 'Y' then 'Capable of: ' when 'F' then 'Not Capable of: ' when 'N' then 'Not Capable of: ' else v_capability.possible || ':' end || v_nl ;

end if;

v_previous_possible := v_capability.possible ;

------------------------------------------------------------- print section body -----------------------------------------------------------declare

v_indented_line_size varchar2(3) := to_char( p_linesize - 5 );

begin

-- print capability name indented 2 spaces

v_output := v_output || v_nl || ' '

|| v_capability.capability_name || v_nl ;

-- print related text indented 4 spaces and word wrapped

if v_capability.related_text is not null then

v_output := v_output || regexp_replace ( v_capability.related_text || ' ' , '(.{1,' || v_indented_line_size || '} |.{1,' || v_indented_line_size || '})' , ' ) ; \1' || v_nl

end if;

-- print message text indented 4 spaces and word wrapped

if v_capability.msgtxt is not null then

v_output := v_output || regexp_replace ( v_capability.msgtxt || ' ' , '(.{1,' || v_indented_line_size || '} |.{1,'

|| v_indented_line_size || '})' , ' ) ; \1' || v_nl

end if;

end;

end loop;

commit ;

return( v_output );

end; /

show errors No errors.

This completes our preparations. Now let's see DBMS_MVIEW.EXPLAIN_VIEW in action. DBMS_MVIEW.EXPLAIN_MVIEW With a Query

DBMS_MVIEW.EXPLAIN_MVIEW can analyze three different types of materialized view code: 1. a defining query 2. a CREATE MATERIALIZED VIEW command 3. an existing materialized view. Here is an example that explains a simple query which could appear as the defining query in a CREATE MATERIALIZED VIEW command.
set long 5000

select my_mv_capabilities( 'SELECT * FROM T', 'REFRESH' ) as mv_report from dual ;

MV_REPORT --------------------------------------------------------------------------------

Capable of:

REFRESH_COMPLETE

Not Capable of:

REFRESH_FAST

REFRESH_FAST_AFTER_INSERT SCOTT.T the detail table does not have a materialized view log

REFRESH_FAST_AFTER_ONETAB_DML see the reason why REFRESH_FAST_AFTER_INSERT is disabled

REFRESH_FAST_AFTER_ANY_DML see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

(Descriptions of each capability name are available at Table 8-7 CAPABILITY_NAME Column Details. A list of messages and related text is available at Table 8-8 MV_CAPABILITIES_TABLE Column Details.) The EXPLAIN_MVIEW output above shows that fast refresh is not possible in this case because T has no materialized view log. Note that DBMS_MVIEW.EXPLAIN_MVIEW can report on a materialized view's refresh, rewrite, and partition change tracking (PCT) capabilities. For now we will only examine refresh capabilities. Rewrite capabilities will be covered in Query Rewrite Restrictions and Capabilities. DBMS_MVIEW.EXPLAIN_MVIEW With CREATE MATERIALIZED VIEW Now let's create a materialized view log on T and then use EXPLAIN_MVIEW to explain the capabilities of an entire CREATE MATERIALIZED VIEW command.
create materialized view log on t ;

select my_mv_capabilities ( 'CREATE MATERIALIZED VIEW MV REFRESH FAST AS SELECT * FROM T' , 'REFRESH' ) as mv_report from dual ;

MV_REPORT --------------------------------------------------------------------------------

Capable of:

REFRESH_COMPLETE

REFRESH_FAST

REFRESH_FAST_AFTER_INSERT

REFRESH_FAST_AFTER_ONETAB_DML

REFRESH_FAST_AFTER_ANY_DML

This time we see that a materialized view using our simple query could be fast refreshable in all cases. DBMS_MVIEW.EXPLAIN_MVIEW With Existing Materialized View For our last example we will explain an existing materialized view, the insertonly one we saw in the preceding topic REFRESH FAST Categories.
create materialized view log on t2 with primary key, rowid, sequence ( t_key, amt ) including new values

create materialized view mv refresh fast as select t_key, max( amt ) amt_max from t2 group by t_key ;

select my_mv_capabilities( 'MV', 'REFRESH' ) as mv_report from dual ;

MV_REPORT --------------------------------------------------------------------------------

Capable of:

REFRESH_COMPLETE

REFRESH_FAST

REFRESH_FAST_AFTER_INSERT

Not Capable of:

REFRESH_FAST_AFTER_ONETAB_DML mv uses the MIN or MAX aggregate functions

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

REFRESH_FAST_AFTER_ANY_DML see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled

Here we see that fast refresh is available after inserts, but not other types of DML. Note also that the "REFRESH_FAST" capability will appear whenever at least one of the other REFRESH_FAST_% capabilities is available. It does not mean the materialized view is fast refreshable in all cases. Gotcha Both the $ORACLE_HOME/rdbms/admin/utlxmv.sql file and the CREATE TABLE command at Oracle Database Data Warehousing Guide - Basic Materialized Views - Using MV_CAPABILITIES_TABLE state the values in MV_CAPABILITIES_TABLE.POSSIBLE will either be "T" or "F".
CREATE TABLE MV_CAPABILITIES_TABLE ... POSSIBLE CHARACTER(1), -- T = capability is possible -- F = capability is not possible ...

In actual use we can see the values are really "Y" and "N".
delete from mv_capabilities_table ;

execute dbms_mview.explain_mview( 'select * from t' );

commit;

column possible format a8

select distinct POSSIBLE from mv_capabilities_table ;

POSSIBLE -------Y N

The values "T" and "F" are, however, used when DBMS_MVIEW.EXPLAIN_MVIEW output is saved to a varray. Cleanup
set long 80

drop materialized view mv ;

drop materialized view log on t ; drop materialized view log on t2 ;

Materialized Views
REFRESH FORCE
In REFRESH FAST Categories and DBMS_MVIEW.EXPLAIN_MVIEW we saw an insert-only materialized view which could be fast refreshed after inserts into the base table but needed a complete refresh after other types of DML. With these types of materialized views it is often most convenient to let Oracle decide which refresh method is best. The REFRESH FORCE method does just that. It performs a FAST refresh if possible, otherwise it performs a COMPLETE refresh.
create materialized view log on t2 with primary key, rowid, sequence ( t_key, amt ) including new values ;

create materialized view mv REFRESH FORCE as select t_key, max( amt ) amt_max from t2 group by t_key ;

select rowid, t_key, amt_max from mv ;

ROWID

T_KEY

AMT_MAX

------------------ ---------- ----------

AAAWpLAAEAAAAaMAAA AAAWpLAAEAAAAaMAAB

1 2

300 250

First let's try an insert and a refresh.


insert into t2 values ( 5, 2, 500 ); commit;

execute dbms_mview.refresh( list => 'MV' );

select rowid, t_key, amt_max from mv ;

ROWID

T_KEY

AMT_MAX

------------------ ---------- ---------AAAWpLAAEAAAAaMAAA AAAWpLAAEAAAAaMAAB 1 2 300 500

Since the rowids did not change but the AMT_MAX values did we can tell that a FAST refresh was performed. Now let's try a delete followed by a refresh.
delete from t2 where key = 5 ; commit;

execute dbms_mview.refresh( list => 'MV' );

select rowid, t_key, amt_max from mv ;

ROWID

T_KEY

AMT_MAX

------------------ ---------- ---------AAAWpLAAEAAAAaMAAC AAAWpLAAEAAAAaMAAD 1 2 300 250

In the REFRESH FAST Categories topic we received an "ORA-32314: REFRESH FAST of "SCOTT"."MV" unsupported after deletes/updates" error at this point. This time with REFRESH FORCE we did not. Instead Oracle performed a COMPLETE refresh (note how the rowids for each row changed). Cleanup
drop materialized view mv ;

drop materialized view log on t2 ;

Materialized Views
NEVER REFRESH
If for some reason we need to prevent refresh operations of any sort, FAST or COMPLETE, on our materialized views we can use theNEVER REFRESH method.
create materialized view mv NEVER REFRESH as select * from t

select * from mv ;

KEY VAL ---------- ----1 a 2 b 3 c 4

Let's see what happens when we update the base table and then attempt a refresh.
update t set val = upper(val) ; commit ;

execute dbms_mview.refresh( 'MV' ); BEGIN dbms_mview.refresh( 'MV' ); END;

* ERROR at line 1: ORA-23538: cannot explicitly refresh a NEVER REFRESH materialized view ("MV") ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2537 ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2743

ORA-06512: at "SYS.DBMS_SNAPSHOT", line 2712 ORA-06512: at line 1

Oracle prevented the refresh by raising an error. I cannot see a practical reason for having a materialized view with NEVER REFRESH set at all times. (If you know of any please let me know using the Comments link below.) NEVER REFRESH can come in handy though when refresh operations on a materialized view need to be prevented temporarily during maintenance or debugging operations. In this case the materialized view's refresh mode can be changed to NEVER REFRESH using the ALTER MATERIALIZED VIEW command. Cleanup
drop materialized view mv ;

update t set val = lower(val) ; commit ;

Materialized Views
ON DEMAND
Up to this point in the tutorial we have always refreshed our materialized views manually with the DBMS_MVIEW.REFRESH command. This is know as ON DEMAND refreshing and it is the default refresh mode when none is specified in the CREATE MATERIALIZED VIEW command. In other words this

create materialized view mv as select * from t ;

is equivalent to this.
drop materialized view mv ;

create materialized view mv REFRESH ON DEMAND as select * from t ;

To refresh ON DEMAND materialized views we explicitly call one of the following procedures. DBMS_MVIEW.REFRESH DBMS_MVIEW.REFRESH_ALL_MVIEWS DBMS_MVIEW.REFRESH_DEPENDENT Here is an example that uses DBMS_MVIEW.REFRESH.

insert into t values ( 5, 'e' ); commit;

select * from mv where key = 5 ;

no rows selected

execute DBMS_MVIEW.REFRESH( 'MV' );

select * from mv where key = 5 ;

KEY VAL ---------- ----5 e

Cleanup
drop materialized view mv ;

delete from t where key = 5 ; commit;

Materialized Views
ON COMMIT
In some situations it would be convenient to have Oracle refresh a materialized view automatically whenever changes to the base table are committed. This is possible using the ON COMMIT refresh mode. Here is an example.
create materialized view log on t ;

create materialized view mv REFRESH FAST ON COMMIT as select * from t ;

select rowid, key, val from mv ;

ROWID

KEY VAL

------------------ ---------- ----AAAXNGAAEAAAAasAAA AAAXNGAAEAAAAasAAB AAAXNGAAEAAAAasAAC AAAXNGAAEAAAAasAAD 1 a 2 b 3 c 4

Let's see what happens to the view in the course of an insert operation.
insert into t values ( 5, 'e' );

select rowid, key, val from mv ;

ROWID

KEY VAL

------------------ ---------- ----AAAXNGAAEAAAAasAAA AAAXNGAAEAAAAasAAB 1 a 2 b

AAAXNGAAEAAAAasAAC AAAXNGAAEAAAAasAAD

3 c 4

Nothing happend yet. Let's issue a COMMIT.


commit;

select rowid, key, val from mv ;

ROWID

KEY VAL

------------------ ---------- ----AAAXNGAAEAAAAasAAA AAAXNGAAEAAAAasAAB AAAXNGAAEAAAAasAAC AAAXNGAAEAAAAasAAD AAAXNGAAEAAAAatAAA 1 a 2 b 3 c 4 5 e

Note how the materialized view was automatically fast refreshed after the COMMIT command. No call to DBMS_MVIEW.REFRESH was required. Restrictions Materialized views can only refresh ON COMMIT in certain situations. 1. The materialized view cannot contain object types or Oracle-supplied types. 2. The base tables will never have any distributed transactions applied to them. The first case produces an error during the CREATE MATERIALIZED VIEW command.

-- this materialized view is not fast refreshable -- because the materialized view contains an Oracle-supplied type

create materialized view mv2 REFRESH FAST ON COMMIT as select key, val, sys_xmlgen( val ) as val_xml from t ; as select key, val, sys_xmlgen( val ) as val_xml from t * ERROR at line 3: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

The second case generates an error when a distributed transaction is attempted on the base table. In the following example materialized view MV (created at the top of this page) was created with REFRESH FAST. Attempting a distributed transaction on its base table, T, will therefore raise an error.
insert into t select key+10, val from T@REMOTE ; commit; commit * ERROR at line 1: ORA-02050: transaction 5.21.5632 rolled back, some remote DBs may be in-doubt ORA-02051: another session in same transaction failed

(REMOTE is a database link which loops back to the current account.) ON DEMAND materialized views have no such restriction, as the following snippet demonstrates.
alter materialized view mv refresh ON DEMAND ;

insert into t select key+10, val from T@REMOTE ; commit;

select * from t ;

KEY VAL ---------- ----1 a 2 b 3 c 4 5 e 11 a 12 b 13 c 14 15 e

-- cleanup test data in preparation for next section

delete from t where key >= 5 ; commit ;

Gotcha The SQL Language Reference manual says this about the ON COMMIT clause. "Specify ON COMMIT to indicate that a fast refresh is to occur whenever the database commits a transaction that operates on a master table of the materialized view." -- Oracle Database SQL Language Reference: CREATE MATERIALIZED VIEW When I first read this I assumed it meant that "REFRESH COMPLETE ON COMMIT" is not allowed. I also assumed that specifying "REFRESH ON COMMIT" is equivalent to specifying "REFRESH FAST ON COMMIT". The following examples prove neither is correct however.
create materialized view mv2 REFRESH COMPLETE ON COMMIT as select key, val from t ;

As we can see the CREATE MATERIALZIED view command succeeded even though COMPLETE, not FAST, was specified with ON COMMIT. The next example examines the behavior of "REFRESH ON COMMIT" without a specified refresh method.

drop materialized view log on t ;

-- fast refreshable materialized views on T can no longer be created on T -- because it has no materialized view log

drop materialized view mv2 ;

create materialized view mv2 REFRESH ON COMMIT as select key, val from t ; select rowid, key, val from mv2 ;

ROWID

KEY VAL

------------------ ---------- ----AAAXNMAAEAAAAakAAA AAAXNMAAEAAAAakAAB AAAXNMAAEAAAAakAAC AAAXNMAAEAAAAakAAD 1 a 2 b 3 c 4

insert into t values ( 5, 'e' ); commit ;

select rowid, key, val from mv2 ;

ROWID

KEY VAL

------------------ ---------- ----AAAXNMAAEAAAAakAAE AAAXNMAAEAAAAakAAF AAAXNMAAEAAAAakAAG AAAXNMAAEAAAAakAAH AAAXNMAAEAAAAakAAI 1 a 2 b 3 c 4 5 e

The fact that all the rowid's in MV2 changed after the INSERT transaction committed confirms that a complete refresh took place during the commit. "REFRESH ON COMMIT" is not therefore equivalent to "REFRESH FAST ON COMMIT". In fact, when no REFRESH method is specified the default behaviour is "REFRESH FORCE" regardless of whether ON COMMIT is used or not. Given these observations I can only conclude the documentation is either in error or misleading when it says "specify ON COMMIT to indicate that a fast refresh is to occur". Cleanup
drop materialized view mv ;

drop materialized view mv2 ;

delete from t where key >= 5 ; commit ;

Materialized Views
Constraints
System Generated Constraints When a materialized view is created Oracle may add system generated constraints to its underlying table (i.e. the table containing the results of the query, not to be confused with a base table). In the following example note how Oracle automatically adds a primary key constraint to the table called "MV", which is part of the materialized view also called "MV".
create materialized view mv as select key, val from t ;

column constraint_name format a20 column constraint_type format a15 column index_name format a15

select constraint_name, constraint_type, index_name from where user_constraints TABLE_NAME = 'MV' ;

CONSTRAINT_NAME

CONSTRAINT_TYPE INDEX_NAME

-------------------- --------------- --------------SYS_C0019948 P SYS_C0019948

In the next example Oracle automatically adds a check constraint.

drop materialized view mv ;

describe t2 Name Null? Type

-------------------------------------------- -------- -----------------------------KEY T_KEY AMT NOT NULL NUMBER NOT NULL NUMBER NOT NULL NUMBER

create materialized view log on t2 with primary key, rowid, sequence ( t_key, amt ) including new values ;

create materialized view mv refresh fast on commit as select t_key, count(*) row_count from t2 group by t_key ;

column search_condition format a30

select constraint_name, constraint_type, search_condition from where user_constraints table_name = 'MV' ;

CONSTRAINT_NAME

CONSTRAINT_TYPE SEARCH_CONDITION

-------------------- --------------- -----------------------------SYS_C0019949 C "T_KEY" IS NOT NULL

Adding Your Own Constraints If necessary we can create our own constraints on materialized view tables in addition to the ones Oracle may add. When the materialized view is in ON COMMIT mode these constraints effectively constrain the materialized view's base tables. Let's see this in action by creating a check constraint on MV.
select * from t2 ;

KEY

T_KEY

AMT

---------- ---------- ---------10 20 30 40 50 1 1 1 2 2 100 300 200 250 150

alter table mv -- note we used "alter table" here

add CONSTRAINT MY_CONSTRAINT CHECK ( ROW_COUNT <= 3 ) DEFERRABLE ;

select constraint_name, constraint_type, search_condition from where user_constraints table_name = 'MV' ;

CONSTRAINT_NAME

CONSTRAINT_TYPE SEARCH_CONDITION

-------------------- --------------- -----------------------------SYS_C0019949 MY_CONSTRAINT C C "T_KEY" IS NOT NULL ROW_COUNT <= 3

Now any attempt to create more than 3 rows per group in table T2 will generate an error at commit time.
insert into T2 values ( 5, 1, 500 ); commit; commit * ERROR at line 1: ORA-12008: error in materialized view refresh path ORA-02290: check constraint (SCOTT.MY_CONSTRAINT) violated

Implementing multirow validation rules such as this one properly is not possible using check constraints on regular tables. Implementing them using triggers can be difficult if not impossible. With materialized views they are declared using a few lines of code and are virtually bullet proof when applied correctly. We will learn more about this powerful multirow validation approach in a future SQL Snippets tutorial so stay tuned! In the mean time Ask Tom "Declarative Integrity" has some good information on the subject. Gotcha When we created MY_CONSTRAINT above we use an ALTER TABLE command. Curiously enough an ALTER MATERIALIZED VIEW command would have worked too.
ALTER MATERIALIZED VIEW mv add constraint my_second_constraint check ( row_count < 4 ) deferrable ;

select constraint_name, constraint_type, search_condition from where user_constraints table_name = 'MV' ;

CONSTRAINT_NAME

CONSTRAINT_TYPE SEARCH_CONDITION

-------------------- --------------- -----------------------------SYS_C0019949 MY_CONSTRAINT C C "T_KEY" IS NOT NULL ROW_COUNT <= 3 row_count < 4

MY_SECOND_CONSTRAINT C

The Oracle manual page for ALTER MATERIALIZED VIEW however does not indicate that constraints can be added this way. Until the documentation says this is legal it is best to use ALTER TABLE.

Cleanup
drop materialized view mv ;

drop materialized view log on t2 ;

Materialized Views
Indexes
When a materialized view is created Oracle may add system generated indexes to its underlying table (i.e. the table containing the results of the query, not to be confused with a base table). In the following example note how Oracle automatically adds an index to implement the system generated primary key we saw in the preceding topic, Constraints.
create materialized view mv as select key, val from t ;

column index_name

format a15

column column_name format a15

select index_name , i.uniqueness , ic.column_name from user_indexes i

inner join user_ind_columns ic using ( index_name ) where i.table_name = 'MV' ;

INDEX_NAME

UNIQUENES COLUMN_NAME

--------------- --------- --------------SYS_C0019959 UNIQUE KEY

In the next example Oracle automatically generates a function based index.


drop materialized view mv ;

create materialized view log on t2 with primary key, rowid, sequence ( t_key, amt ) including new values ;

create materialized view mv refresh fast on commit as select t_key, COUNT(*) ROW_COUNT from t2 group by t_key

column column_expression format a35

select index_name , i.uniqueness , ic.column_name , ie.column_expression from user_indexes i inner join user_ind_columns ic left outer join user_ind_expressions ie using ( index_name ) using ( index_name ) where ic.table_name = 'MV' ;

INDEX_NAME

UNIQUENES COLUMN_NAME

COLUMN_EXPRESSION

--------------- --------- --------------- ----------------------------------I_SNAP$_MV UNIQUE SYS_NC00003$ SYS_OP_MAP_NONNULL("T_KEY")

(Note that SYS_OP_MAP_NONNULL is an undocumented Oracle function. Do not attempt to use it in your own code. See Nulls and Equality: SQL Only for additional info.) Adding Your Own Indexes We can add out own indexes to MV just as we would a regular table. In the following example we will add an index on the T_KEY column.
create index MY_INDEX on mv ( T_KEY ) ;

select index_name , i.uniqueness , ic.column_name from user_indexes i inner join user_ind_columns ic using ( index_name ) where i.table_name = 'MV' ;

INDEX_NAME

UNIQUENES COLUMN_NAME

--------------- --------- --------------I_SNAP$_MV MY_INDEX UNIQUE SYS_NC00003$

NONUNIQUE T_KEY

To confirm that Oracle uses our index in queries let's turn SQL*Plus's Autotrace feature on and execute a query.
set autotrace on explain set linesize 95

select * from where mv t_key = 2 ;

T_KEY

ROW_COUNT

---------- ---------2 2

Execution Plan ---------------------------------------------------------Plan hash value: 2793437614

------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

------------------------------------------------------------------------------------------| | |* 0 | SELECT STATEMENT 1 | 2 | | | | 1 | 1 | 1 | 26 | 26 | | 2 2 1 (0)| 00:00:01 | (0)| 00:00:01 | (0)| 00:00:01 |

MAT_VIEW ACCESS BY INDEX ROWID| MV INDEX RANGE SCAN

| MY_INDEX |

-------------------------------------------------------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

2 - access("T_KEY"=2)

Note ----- dynamic sampling used for this statement

Note how the optimizer chose an INDEX RANGE SCAN from MY_INDEX in step 2. Cleanup
drop materialized view mv ;

drop materialized view log on t2 ;

Materialized Views
ENABLE QUERY REWRITE
Materialized views can be useful for pre-calculating and storing derived values such as AMT_MAX in the following snippet.
create materialized view log on t2 with primary key, rowid, sequence ( t_key, amt ) including new values ;

create materialized view mv refresh fast on commit

as select t_key, MAX( AMT ) AMT_MAX from t2 group by t_key ;

Such materialized views make queries like this


select t_key, amt_max FROM MV order by t_key ;

T_KEY

AMT_MAX

---------- ---------1 2 300 250

faster than its equivalent query.


select t_key, max( amt ) as amt_max FROM T2 group by t_key order by t_key ;

T_KEY

AMT_MAX

---------- ---------1 2 300 250

Wouldn't it be nice if Oracle could use the information in MV to resolve this last query too? If your database has a feature called Query Rewrite available and enabled this happens automatically. To see it in action we first need to make the materialized view available to Query Rewrite like this.
alter materialized view mv ENABLE QUERY REWRITE ;

(See Gotcha - ORA-00439 below if you encounter an ORA-00439 error at this step.) Note that materialized views which do not include the ENABLE QUERY REWRITE clause will have Query Rewrite disabled by default. Next we collect statistics on the materialized view to help Oracle optimize the query rewrite process.
execute dbms_stats.gather_table_stats( user, 'MV' ) ;

Finally we can confirm Oracle will use the materialized view in queries by turning SQL*Plus's Autotrace feature on.
set autotrace on explain set linesize 95

select t_key, max( amt ) as amt_max FROM T2 group by t_key order by t_key ;

T_KEY

AMT_MAX

---------- ---------1 2 300 250

Execution Plan ---------------------------------------------------------Plan hash value: 446852971

-------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------------------| | | 0 | SELECT STATEMENT 1 | 2 | SORT ORDER BY | | | | | 2 | 2 | 2 | 14 | 14 | 14 | 4 4 3 (25)| 00:00:01 | (25)| 00:00:01 | (0)| 00:00:01 |

MAT_VIEW REWRITE ACCESS FULL| MV

--------------------------------------------------------------------------------------

Note how the optimizer chose to access MV for its pre-calculated MAX(AMT) values in line 2 even though the query itself made no mention of MV. Without the Query Rewrite feature the execution plan would look like this.
alter session set QUERY_REWRITE_ENABLED = FALSE ;

select t_key, max( amt ) as amt_max

FROM T2 group by t_key order by t_key ;

T_KEY

AMT_MAX

---------- ---------1 2 300 250

Execution Plan ---------------------------------------------------------Plan hash value: 50962384

--------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

--------------------------------------------------------------------------| | | 0 | SELECT STATEMENT 1 | 2 | SORT GROUP BY | | | | | 5 | 5 | 5 | 130 | 130 | 130 | 4 4 3 (25)| 00:00:01 | (25)| 00:00:01 | (0)| 00:00:01 |

TABLE ACCESS FULL| T2

---------------------------------------------------------------------------

Note ----- dynamic sampling used for this statement

Note how the optimizer chose to access T2 this time. Each time this query is executed it has to re-calculate MAX(VAL) from the information in T2 for each

group, a more expensive approach than simply selecting pre-calculated column values from MV is. Gotcha - ORA-00439 The materialized view query rewrite feature is not available in Oracle XE and some other Oracle configurations. If you attempt to use ENABLE QUERY REWITE in an Oracle database where the feature is not enabled you will receive an ORA-00439 error.
create materialized view mv2 refresh fast on commit ENABLE QUERY REWRITE as select t_key , count(*) as row_count ,

count(amt) as amt_count from t2 group by t_key ; from t2 * ERROR at line 9: ORA-00439: feature not enabled: Materialized view rewrite

Cleanup
alter session set query_rewrite_enabled = true ;

set autotrace off

drop materialized view mv ;

drop materialized view log on t2 ;

Materialized Views
Query Rewrite Restrictions and Capabilities
Restrictions Materialized views with the following characteristics cannot have query rewrite enabled: the defining query references functions which are not DETERMINISTIC an expression in the defining query is not repeatable; e.g. an expression containing the USER pseudo column or the SYSTIMESTAMP function. Attempting to violate these restrictions results in an error.

create materialized view mv ENABLE QUERY REWRITE as select key, val, USER from t ; as select key, val, USER from t * ERROR at line 3: ORA-30353: expression not supported for query rewrite

Capabilities A few different materialized view query rewrite capabilities exist. In EXPLAIN_MVIEW we used a utility called MY_MV_CAPABILITIES to explore a materialized view's refresh capabilities. In the snippets below we will use this same utility to explore rewrite capabilities. First lets look at a simple, single table materialized view with query rewrite disabled.
create materialized view mv DISABLE QUERY REWRITE as select key, val from t ;

set long 5000

select my_mv_capabilities( 'MV', 'REWRITE' ) as mv_report from dual ;

MV_REPORT --------------------------------------------------------------------------------

Not Capable of:

REWRITE

REWRITE_FULL_TEXT_MATCH query rewrite is disabled on the materialized view

REWRITE_PARTIAL_TEXT_MATCH query rewrite is disabled on the materialized view

REWRITE_GENERAL query rewrite is disabled on the materialized view

This materialized view obviously has no rewrite capabilities available to it. (Descriptions of each capability name are available at Table 8-7 CAPABILITY_NAME Column Details.) Enabling query rewrite on the materialized view changes this.
alter materialized view mv ENABLE QUERY REWRITE ;

select my_mv_capabilities( 'MV', 'REWRITE' ) as mv_report from dual ;

MV_REPORT --------------------------------------------------------------------------------

Capable of:

REWRITE

REWRITE_FULL_TEXT_MATCH

REWRITE_PARTIAL_TEXT_MATCH

REWRITE_GENERAL

Now all rewrite capabilities are available. If the materialized view happened to referenced a remote table then some rewrite capabilities would be available, but not others.
drop materialized view mv ;

create materialized view mv enable query rewrite as select key, val from T@REMOTE ;

select my_mv_capabilities( 'MV', 'REWRITE' ) as mv_report from dual ;

MV_REPORT --------------------------------------------------------------------------------

Capable of:

REWRITE

REWRITE_PARTIAL_TEXT_MATCH

REWRITE_GENERAL

Not Capable of:

REWRITE_FULL_TEXT_MATCH T mv references a remote table or view in the FROM list

Cleanup
drop materialized view mv ;

Das könnte Ihnen auch gefallen