CHAPTER 15 Materialized Views Materialized Views (MVs) • Sometimes people new to MVs are confused by the term “materialized view”. • An MV is basically a table that is periodically refreshed with data from a SQL query. • Oracle provides numerous configurations and management features for MVs. • Fairly robust and flexible feature that has been available since version 7. Primary Uses for MVs • Replicating of data to offload query workloads to separate reporting databases. • Improving performance of queries by periodically computing and storing the results of complex aggregations of data, which lets users query point-in-time results (of the complex queries). Basic Materialized View Create Statement • This is a complete refresh, on demand MV based on query that selects from the SALES table: create materialized view sales_mv segment creation immediate refresh complete on demand as select sales_amt ,sales_dtt from sales; Using the Complete Refresh Mechanism • Data is deleted from the underlying table. • MV is completely refreshed with data from the base table. SQL> exec dbms_mview.refresh('SALES_MV','C'); Complete Refresh Architectural Steps 1. Users/applications create transactions. 2. Data is committed in the base table. 3. A complete refresh is manually initiated with the DBMS_MVIEW package. 4. Data in the underlying MV is deleted and completely refreshed with the contents of the base table. 5. Users can query data from the MV, which contains a point-in-time snapshot of the base table’s data. Complete Refresh Architectural Components Creating a Fast Refreshable MV 1. 2. 3. Create a base table (if it’s not already created). Create an MV log on the base table. Create an MV as fast-refreshable. SQL> create materialized view log on sales with primary key; create materialized view sales_mv segment creation immediate refresh with primary key fast on demand as select sales_id, sales_amt, sales_dtt from sales; Using the Fast Refresh Mechanism • MV log is a table that stores incremental changes to the data in the base table. • MV log is created on the base table. • Since the base table has an MV log, the MV can be fast refreshed, meaning that only the data changes since the last time the MV was refreshed need to be applied. SQL> exec dbms_mview.refresh('SALES_MV','F'); Fast Refresh Architecture Steps 1. Users create transactions. 2. Data is committed in the base table. 3. An internal trigger on the base table populates the MV log table. 4. A fast refresh is initiated via the DBMS_MVIEW package. 5. DML changes that have been created since the last refresh are applied to the MV. Rows no longer needed by the MV are deleted from the MV log. 6. Users can query data from the MV, which contains a point-in-time snapshot of the base table’s data. Fast Refresh Architectural Components MV and MV Log are instantiated as tables • Most database features that apply to tables can be applied to MVs and MV logs: • Storage and tablespace placement • Indexing • Partitioning • Compression • Encryption • Logging • Parallelism Specifying Storage Attributes for an MV create materialized view inv_mv pctused 95 pctfree 5 tablespace mv_data using index tablespace mv_index as select inv_id ,inv_desc from inv; Creating Indexes on MVs • MV stores data in a database table. • You can create indexes on this MV to improve performance. • For example, if there is a column in an MV that is heavily referenced in WHERE clauses of SQL statements, then consider creating an index on this column (especially if it is highly selectable): SQL> create index inv_mv_idx1 on inv_mv(region_id) tablespace mv_index; Partitioning Materialized Views • You can partition an MV table like any other regular table in the database. • If you work with large MVs, you may want to consider partitioning to better manage and maintain a large table. • Use the PARTITION clause when you create the MV. • This example builds an MV that is partitioned by range on the DATE_ID column: create materialized view inv_mv partition by range (date_id) (partition p1 values less than (20100101) ,partition p2 values less than (20110101) ,partition p3 values less than (20120101)) refresh on demand complete with rowid as select inv_id, inv_desc,date_id from inv; Compressing a Materialized View • When you create an MV, an underlying table is created to store the data. • Because this table is a regular database table, you can implement features such as compression. For example: create materialized view inv_mv compress as select inv_id ,inv_desc from inv; Encrypting Materialized View Columns • When you create an MV, an underlying table is created to store the data. • Because this table is a regular database table, you can implement features such as encryption of columns. For example: create materialized view inv_mv (inv_id encrypt no salt ,inv_desc encrypt) as select inv_id inv_id ,inv_desc inv_desc from inv; Building a Materialized View on a Prebuilt Table • In data-warehouse environments, sometimes you need to create a table, populate it with large quantities of data, and then transform it into an MV. Listed next are the steps for building an MV on a prebuilt table: 1. Create a table. 2. Populate it with data. 3. Create an MV on the table created in step 1. Creating an Unpopulated Materialized View • When you create an MV, you have the option of instructing Oracle whether or not to initially populate the MV with data. • For example, if it takes several hours to initially build an MV, you may want to first define the MV and then populate it as a separate job. • Use the BUILD DEFERRED clause to instruct Oracle not to initially populate the MV with the results of the query: create materialized view inv_mv tablespace mv_data build deferred refresh complete on demand as select inv_id ,inv_desc from inv; Creating a Materialized View Refreshed on Commit • Use ON COMMIT when you need the data from the master table immediately reflected in the MV. • Consider the overhead and availability issues before implementing this type of MV. create materialized view inv_mv refresh on commit as select inv_id, inv_desc from inv; Creating a Never-Refreshable Materialized View • You may never want an MV to be refreshed. For example, you may want to guarantee that you have a snapshot of table at a point in time for auditing purposes. • Specify the NEVER REFRESH clause when you create the MV to achieve this: • create materialized view inv_mv tablespace mv_data using index tablespace mv_index never refresh as select inv_id ,inv_desc from inv; Creating Materialized Views for QueryRewrite • Query rewrite allows the optimizer to recognize that an MV can be used to fulfill the requirements of a query instead of using the underlying master (base) tables. • When users frequently write their own queries and are unaware of the available MVs, this feature can help greatly with performance. create materialized view sales_mv tablespace mv_data using index tablespace mv_index enable query rewrite as select sum(sales_amt), b.reg_desc from sales a, region b where a.region_id = b.region_id group by b.reg_desc; Creating a Fast-Refreshable MV Based on a Complex Query • In many situations, when you base an MV on a query that joins multiple tables, it’s deemed complex, and therefore is available only for a complete refresh. • However, in some scenarios, you can create a fastrefreshable MV when you reference two tables that are joined together in the MV query. • Use the EXPLAIN_MVIEW procedure of the DBMS_MVIEW to determine whether it’s possible to fastrefresh a complex query. Viewing Materialized View DDL • To quickly view the SQL query on which an MV is based, select from the QUERY column of DBA/ALL/USER_MVIEWS. • If you’re using SQL*Plus, first set the LONG variable to a value large enough to display the entire contents of a LONG column: SQL> set long 5000 SQL> select query from dba_mviews where mview_name=UPPER('&&mview_name'); • To view the entire Data Definition Language ()DDL required to re-create an MV, use the DBMS_METADATA package: SQL> select dbms_metadata.get_ddl('MATERIALIZED_VIEW','INV_MV') from dual; Dropping a Materialized View • You may occasionally need to drop an MV. Perhaps a view is no longer being used, or you need to drop and recreate an MV to change the underlying query on which the MV is based (such as adding a column to it). • Use the DROP MATERIALIZED VIEW command to drop an MV. SQL> drop materialized view orders_mv; Re-creating a Materialized View to Reflect Base-Table Modifications • There is no ALTER MATERIALIZED VIEW ADD/DROP/MODIFY <column> statement, you must do the following to add/delete columns in an MV: 1. Alter the base table. 2. Drop and re-create the MV to reflect the changes in the base table. Altering a Materialized View but Preserving the Underlying Table • When you drop an MV, you have the option of preserving the underlying table and its data. • You may find this approach advantageous when you’re working with large MVs in data-warehouse environments. Here are the steps: 1. Alter the base table. 2. Drop the MV, but preserve the underlying table. 3. Modify the underlying table. 4. Re-create the MV using the ON PREBUILT TABLE clause. Altering a Materialized View Created on a Prebuilt Table • If you originally created an MV using the ON PREBUILT TABLE clause, then here are the steps for modifying an MV that was created using the ON PREBUILT TABLE clause: 1. Alter the base table. 2. Drop the MV. For MVs built on prebuilt tables, this doesn’t drop the underlying table. 3. Alter the prebuilt table. 4. Re-create the MV on the prebuilt table. Toggling Redo Logging on a Materialized View • In some scenarios you can easily recreate an MV, and know that you don’t ever need to recover any of its data (because you can always re-create from the master table). • By default, redo logging is enabled when you create an MV. You have the option of specifying that redo not be logged when an MV is refreshed: create materialized view inv_mv nologging tablespace mv_data using index tablespace mv_index as select inv_id ,inv_desc from inv; Altering Parallelism • Sometimes an MV is created with a high degree of parallelism to improve the performance of the creation process. SQL> alter materialized view inv_mv parallel 3; Managing Materialized View Logs • MV logs are required for fast-refreshable MVs. • The MV log is a table that stores DML information for a • • • • master (base) table. It’s created in the same database as the master table with the same user that owns the master table. You need the CREATE TABLE privilege to create an MV log. The MV log is populated by an Oracle internal trigger (that you have no control over). This internal trigger inserts a row into the MV log after an INSERT, UPDATE, or DELETE on the master table. Creating a Materialized View Log • Fast-refreshable views require an MV log to be created on the master (base) table. • Use the CREATE MATERIALIZED VIEW LOG command to create an MV log. • This example creates an MV log on the USERS table, specifying that the primary key should be used to identify rows in the MV log: SQL> create materialized view log on users with primary key; Indexing Materialized View Log Columns • Sometimes you may need better performance from your fast-refreshing MVs. • One way to do this is through indexes on columns of the MV log table. In particular, consider indexing the primarykey column and the SNAPTIME$$ column. • Oracle potentially uses two columns in WHERE clauses when refreshing an MV or purging the MV log. Here are examples of creating indexes on MV log columns: SQL> create index mlog$_inv_idx1 on mlog$_inv(snaptime$$) tablespace mv_index; SQL> create index mlog$_inv_idx2 on mlog$_inv(inv_id) tablespace mv_index; Viewing Space Used by a Materialized View Log • You should consider periodically checking the space consumed by an MV log. • If the space consumed is growing (and never shrinking), you may have an issue with an MV not successfully refreshing (and hence causing the MV log never to be purged). • Here’s a query to check the space of MV logs: select segment_name ,tablespace_name ,bytes/1024/1024 meg_bytes ,extents from dba_segments where segment_name like 'MLOG$%' order by meg_bytes; Shrinking the Space in a Materialized View Log • You may encounter the scenario where the MV log wasn’t purged for some reason. • After resolving the issue you notice that the MV log has grown to a large size, and you want to shrink so as to free up the space it’s unnecessarily consuming: SQL> alter table mlog$_registrations enable row movement; SQL> alter materialized view log on registrations shrink space; Checking the Row Count of a Materialized View Log • One way of detecting whether an MV log isn’t being purged is to periodically check the row counts of the MV log tables. • The following query uses SQL to generate SQL that creates a script that checks row counts for MV log tables owned by the currently connected user: set head off pages 0 lines 132 trimspool on spo mvcount_dyn.sql select 'select count(*) || ' || '''' || ': ' || table_name || '''' || ' from ' || table_name || ';' from user_tables where table_name like 'MLOG%'; spo off; Moving a Materialized View Log • If any MV log tables need to be relocated, use the ALTER MATERIALIZED VIEW LOG ON <table_name> MOVE statement. • Specify the name of the master table (and not the underlying MLOG$ table) on which the MV is created: SQL> alter materialized view log on inv move tablespace tbsp2; Dropping a Materialized View Log • Use the DROP MATERIALIZED VIEW LOG ON statement to drop an MV log. • You don’t need to know the name of the MV log, but you do need to know the name of the master table on which the log was created. SQL> drop materialized view log on inv; Refreshing Materialized Views • Sooner or later, you’ll need to refresh an MV manually. • Usually this is because you’re testing a refresh or troubleshooting an issue. • To do so, use SQL*Plus to call the REFRESH procedure of the DBMS_MVIEW package. • The procedure takes two parameters: the MV name and the refresh method. • This example uses the EXEC[UTE] statement to call the procedure. The MV being refreshed is INV_MV, and the refresh method is F (for fast): SQL> exec dbms_mview.refresh('INV_MV','F'); Automating Refreshes Using a Shell Script and Scheduling Utility • Many MVs must be refreshed on a daily basis. • To achieve this, you can use a Linux/Unix utility such as cron that calls a shell script to refresh the MVs. • This approach • Is easy to implement and maintain • Makes it easy to create a daily log file for auditing • Sends e-mail when the job has problems or when the database isn’t available Creating an MV with a Refresh Interval • When you initially create an MV, you have the option of specifying START WITH and NEXT clauses that instruct Oracle to set up an internal database job (via the DBMS_JOB package) to initiate the refresh of an MV on a periodic basis. • If you omit START WITH and NEXT, then no job is set up, and you have to use another technique (such as a scheduling utility like cron). • Example: create materialized view inv_mv refresh start with sysdate+1/1440 next sysdate+1 as select inv_id, inv_desc from inv; Efficiently Performing a Complete Refresh • Instruct Oracle to truncate the MV instead of use delete. • This may be desirable for large MV refreshes where it’s okay for the data not to be available after the truncate and before the MV is completely refreshed. • You have the option of instructing Oracle to perform the removal of data as efficiently as possible via the ATOMIC_REFRESH parameter. • When this parameter is set to FALSE, it allows Oracle to use a TRUNCATE statement instead of a DELETE when performing a complete refresh: SQL> exec dbms_mview.refresh('INV_MV',method=>'C',atomic_refres h=>false); Handling the ORA-12034 Error • The ORA-12034 error is thrown when Oracle determines that the MV log was created after the last refresh took place in the associated MV. • In these scenarios try to perform a complete refresh of the MV. • There are several possible causes for this situation: • The MV log was dropped and re-created. • The MV log was purged. • The master table was reorganized. • The master table was truncated. • The previous refresh failed. Viewing Materialized Views’ Last Refresh Times • If you work MVs, then you need to become familiar with various views used for troubleshooting. • Viewing the last refresh time is oftentimes where you start the troubleshooting process: select mview_name ,to_char(last_refresh_date,'dd-mon-yy hh24:mi:ss') ,refresh_mode ,refresh_method from user_mviews order by 2; Determining Whether a Refresh Is in Progress • When troubleshooting, here’s a handy query which shows which MVs are currently refreshing: select sid ,serial# ,currmvowner ,currmvname from v$mvrefresh; Monitoring Real-Time Refresh Progress • The following query is extremely useful for troubleshooting MV refresh issues. • It shows where the MV is in the refresh process: select currmvowner_knstmvr || '.' || currmvname_knstmvr "MVIEW BEING REFRESHED", decode(reftype_knstmvr, 1, 'FAST', 2, 'COMPLETE', 'UNKNOWN') reftype, decode(groupstate_knstmvr, 1, 'SETUP', 2, 'INSTANTIATE', 3, 'WRAPUP', 'UNKNOWN' ) STATE, total_inserts_knstmvr inserts, total_updates_knstmvr updates, total_deletes_knstmvr deletes from x$knstmvr x where type_knst = 6 and exists (select 1 from v$session s where s.sid=x.sid_knst Understanding Remote-Refresh Architectures • There are numerous ways to snap together various MVs to replicate data. Here is an example: Determining How Many MVs Reference a Central MV Log • If an MV was dropped and unable to un-register itself from a master MV log table, then records grow indefinitely in the master MV log table. • To resolve this issue, you need information regarding which MVs are tied to which MV logs. • This query displays the master-table owner information and the SNAPID (MV ID) of all dependent MVs: select mowner ,master base_table ,snapid ,snaptime from sys.slog$; Creating a Materialized View Group • Sometimes for read consistency you’ll want to refresh a group of MVs. • You use the MAKE procedure of the DBMS_REFRESH package to create an MV group. • When you create an MV group, you must specify a name, a commaseparated list of MVs in the group, the next date to refresh, and the interval used to calculate the next refresh time. • Here’s an example of a group that consists of two MVs: begin dbms_refresh.make( name => 'INV_GROUP' ,list => 'INV_MV, REGION_MV' ,next_date => sysdate-100 ,interval => 'sysdate+1' ); end; / Determining Materialized Views in a Group • When troubleshooting MV groups, it’s useful to start by displaying what the groups are and what MVs are in each group: select a.owner ,a.name mv_group ,b.name mv_name from dba_rgroup a ,dba_rchild b where a.refgroup = b.refgroup and a.owner = b.owner order by a.owner, a.name, b.name; Summary • MVs are a very powerful feature that allows you to aggregate information for improving performance and also for replicating data between database environments. • MVs are a feature rich utility and as a DBA and developer you should be aware of this tool and how to implement and manage these objects.