Beruflich Dokumente
Kultur Dokumente
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 ;
KEY VAL
ROWID
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' );
KEY VAL
ROWID
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;
KEY VAL
ROWID
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' );
KEY VAL
ROWID
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 ;
KEY VAL
ROWID
AAAWnBAAEAAAAaMAAD
KEY VAL
ROWID
Note how none of the rowids in MV changed, indicating a fast refresh. Cleanup
drop materialized view mv ;
Materialized Views
COUNT(*) ---------0
COUNT(*) ---------1
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
COUNT(*) ---------1
execute DBMS_MVIEW.PURGE_LOG( master => 'T', num => 9999, flag => 'delete' ) ;
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
select * from mv ;
Cleanup
delete from t where key = 5 ; commit;
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
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 ;
ROWID
T_KEY
AMT_MAX
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;
ROWID
T_KEY
AMT_MAX
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' );
ROWID
T_KEY
AMT_MAX
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 ;
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
--------------------------------------------------------------------------------- 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_capabilities sys.ExplainMVArrayType ;
v_output clob ;
begin
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 ,
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 ;
begin
|| v_capability.capability_name || v_nl ;
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;
v_output := v_output || regexp_replace ( v_capability.msgtxt || ' ' , '(.{1,' || v_indented_line_size || '} |.{1,'
end if;
end;
end loop;
commit ;
return( v_output );
end; /
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
MV_REPORT --------------------------------------------------------------------------------
Capable of:
REFRESH_COMPLETE
REFRESH_FAST
REFRESH_FAST_AFTER_INSERT SCOTT.T the detail table does not have a materialized view log
(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 ;
MV_REPORT --------------------------------------------------------------------------------
Capable of:
REFRESH_COMPLETE
REFRESH_FAST
REFRESH_FAST_AFTER_INSERT
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 ;
commit;
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
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 ;
ROWID
T_KEY
AMT_MAX
AAAWpLAAEAAAAaMAAA AAAWpLAAEAAAAaMAAB
1 2
300 250
ROWID
T_KEY
AMT_MAX
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;
ROWID
T_KEY
AMT_MAX
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 ;
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 ;
Let's see what happens when we update the base table and then attempt a refresh.
update t set val = upper(val) ; commit ;
* 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
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 ;
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
is equivalent to this.
drop materialized view mv ;
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.
no rows selected
Cleanup
drop materialized view mv ;
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 ;
ROWID
KEY VAL
Let's see what happens to the view in the course of an insert operation.
insert into t values ( 5, 'e' );
ROWID
KEY VAL
AAAXNGAAEAAAAasAAC AAAXNGAAEAAAAasAAD
3 c 4
ROWID
KEY VAL
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 ;
select * from t ;
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.
-- fast refreshable materialized views on T can no longer be created on T -- because it has no materialized view log
create materialized view mv2 REFRESH ON COMMIT as select key, val from t ; select rowid, key, val from mv2 ;
ROWID
KEY VAL
ROWID
KEY VAL
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 ;
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
CONSTRAINT_NAME
CONSTRAINT_TYPE INDEX_NAME
-------------------------------------------- -------- -----------------------------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 ;
CONSTRAINT_NAME
CONSTRAINT_TYPE SEARCH_CONDITION
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
CONSTRAINT_NAME
CONSTRAINT_TYPE SEARCH_CONDITION
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 ;
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 ;
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
INDEX_NAME
UNIQUENES COLUMN_NAME
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
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
(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
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
T_KEY
ROW_COUNT
---------- ---------2 2
| MY_INDEX |
-------------------------------------------------------------------------------------------
2 - access("T_KEY"=2)
Note how the optimizer chose an INDEX RANGE SCAN from MY_INDEX in step 2. Cleanup
drop materialized view mv ;
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 ;
T_KEY
AMT_MAX
T_KEY
AMT_MAX
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
-------------------------------------------------------------------------------------| | | 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 |
--------------------------------------------------------------------------------------
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 ;
T_KEY
AMT_MAX
--------------------------------------------------------------------------| | | 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 |
---------------------------------------------------------------------------
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 ;
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 ;
MV_REPORT --------------------------------------------------------------------------------
REWRITE
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 ;
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 ;
MV_REPORT --------------------------------------------------------------------------------
Capable of:
REWRITE
REWRITE_PARTIAL_TEXT_MATCH
REWRITE_GENERAL
Cleanup
drop materialized view mv ;