Brief introduction into Materialized Views

This week, I am teaching an Oracle Database 11g Data Warehouse Administration course in Munich. One of the focus areas of that course are Materialized Views, and I have developed some examples for that course that I like to share with the Oracle community. Other themes of that four days course are Partitioning, ETL and Parallelization.

Materialized Views have the ability to speed up queries (even dramatically) while being transparent for the queries in a similar way as indexes resp. partitioned tables are. That means that we do not have to modify our queries in order to benefit from these structures. Unlike an ordinary view which is only a stored select statement that runs if we use the view, a materialized view stores the result set of the select statement as a container table. Let me demonstrate the benefit of that. I have a (for my little database) relatively large table and do an aggregation query on it:

SQL> desc sales
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 PROD_ID                                   NOT NULL NUMBER
 CUST_ID                                   NOT NULL NUMBER
 TIME_ID                                   NOT NULL DATE
 CHANNEL_ID                                NOT NULL NUMBER
 PROMO_ID                                  NOT NULL NUMBER
 QUANTITY_SOLD                             NOT NULL NUMBER(10,2)
 AMOUNT_SOLD                               NOT NULL NUMBER(10,2)

SQL> select count(*) from sales;

 COUNT(*)
----------
 7350744

SQL> select bytes/1024/1024 as mb from user_segments where segment_name='SALES';

 MB
----------
 286

SQL> show sga

Total System Global Area  373293056 bytes
Fixed Size                  1219496 bytes
Variable Size             134218840 bytes
Database Buffers          234881024 bytes
Redo Buffers                2973696 bytes

SQL> set timing on
SQL> select channel_id,sum(amount_sold) from sales group by channel_id;

CHANNEL_ID SUM(AMOUNT_SOLD)
---------- ----------------
 2        210770739
 4        109654416
 3        463002085
 9       2219410.08

Elapsed: 00:00:04.51

As you can see, the aggregation takes round 5 seconds. Now I will speed that up using a Materialized View:

SQL> create materialized view mv1 enable query rewrite
 2  as select channel_id,sum(amount_sold) from sales group by channel_id;

Materialized view created.

Elapsed: 00:00:05.69
SQL> select channel_id,sum(amount_sold) from sales group by channel_id;

CHANNEL_ID SUM(AMOUNT_SOLD)
---------- ----------------
 2        210770739
 4        109654416
 3        463002085
 9       2219410.08

Elapsed: 00:00:00.01

The very same statement now takes way less time! Why is that so?

SQL> set autotrace on explain
SQL> select channel_id,sum(amount_sold) from sales group by channel_id;

CHANNEL_ID SUM(AMOUNT_SOLD)
---------- ----------------
 2        210770739
 4        109654416
 3        463002085
 9       2219410.08

Execution Plan
----------------------------------------------------------
Plan hash value: 2958490228
--------------------------------------------------------------------------------
| Id  | Operation                    | Name | Rows  | Bytes | Cost (%CPU)| Time
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |      |     4 |   104 | 3   (0)| 00:00:01
|   1 |  MAT_VIEW REWRITE ACCESS FULL| MV1  |     4 |   104 | 3   (0)| 00:00:01
--------------------------------------------------------------------------------

Because the optimizer transparently rewrote the query on the Materialized View! But what if we now change the content of the base table sales?

SQL> update sales set amount_sold=amount_sold+1 where rownum<2;

1 row updated.

SQL> commit;

Commit complete.

The materialized view is now stale and will no longer be used for query rewrite (as we can already determine by query runtime):

SQL> select mview_name,staleness from user_mviews;

MVIEW_NAME                     STALENESS
------------------------------ -------------------
MV1                            NEEDS_COMPILE

SQL> set timing on
SQL> select channel_id,sum(amount_sold) from sales group by channel_id;

CHANNEL_ID SUM(AMOUNT_SOLD)
---------- ----------------
 2        210770739
 4        109654416
 3        463002086
 9       2219410.08

Elapsed: 00:00:04.52

In order to get statements rewritten against the materialized view again, we must refresh it by some method.

One method is on demand with a procedure call like in this example:

SQL> exec dbms_mview.refresh(list=>'MV1',method=>'C')

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.62

SQL> select channel_id,sum(amount_sold) from sales group by channel_id;

CHANNEL_ID SUM(AMOUNT_SOLD)
---------- ----------------
 2        210770739
 4        109654416
 3        463002086
 9       2219410.08

Elapsed: 00:00:00.01

Now the MV1 can get used again, as we see by runtime. That was a complete refresh – which can take a long time. We would like to get this done faster.

One comfortable method to get a fast refreshable materialized view is the usage of the package DBMS_ADVISOR:

SQL> vari t varchar2(50)
SQL> begin
 2   dbms_advisor.tune_mview(task_name=>:t,
 3                           mv_create_stmt=>'create materialized view mv1'
 4                               || ' refresh fast as'
 5                               || ' select channel_id,sum(amount_sold)'
 6                               || ' from sales group by channel_id');
 7  end;
 8/  
PL/SQL procedure successfully completed.

SQL> set long 5000
SQL> select statement from user_tune_mview where task_name=:t order by action_id;

STATEMENT
--------------------------------------------------------------------------------
CREATE MATERIALIZED VIEW LOG ON "ADAM"."SALES" WITH ROWID, SEQUENCE ("CHANNEL_ID
","AMOUNT_SOLD")  INCLUDING NEW VALUES

ALTER MATERIALIZED VIEW LOG FORCE ON "ADAM"."SALES" ADD ROWID, SEQUENCE ("CHANNE
L_ID","AMOUNT_SOLD")  INCLUDING NEW VALUES

CREATE MATERIALIZED VIEW ADAM.MV1   REFRESH FAST WITH ROWID DISABLE QUERY REWRIT
E AS SELECT ADAM.SALES.CHANNEL_ID C1, SUM("ADAM"."SALES"."AMOUNT_SOLD") M1, COUN
T("ADAM"."SALES"."AMOUNT_SOLD") M2, COUNT(*) M3 FROM ADAM.SALES GROUP BY ADAM.SA
LES.CHANNEL_ID

STATEMENT
--------------------------------------------------------------------------------
DROP MATERIALIZED VIEW ADAM.MV1

SQL> exec dbms_advisor.delete_task(:t)
PL/SQL procedure successfully completed.

Usually, we need Materialized View Logs on the base tables and special expressions in the materialized view query to be able to do a fast refresh.

I simply take the coding from above now:

SQL> CREATE MATERIALIZED VIEW LOG ON "ADAM"."SALES"
WITH ROWID, SEQUENCE ("CHANNEL_ID","AMOUNT_SOLD")
INCLUDING NEW VALUES;  2    3

Materialized view log created.

SQL> CREATE MATERIALIZED VIEW ADAM.MV1
 REFRESH FAST WITH ROWID
 enable QUERY REWRITE AS
 SELECT ADAM.SALES.CHANNEL_ID C1,
 SUM("ADAM"."SALES"."AMOUNT_SOLD") M1,
 COUNT("ADAM"."SALES"."AMOUNT_SOLD") M2,
 COUNT(*) M3
 FROM ADAM.SALES
 GROUP BY ADAM.SALES.CHANNEL_ID;

Materialized view created.

After again modifying the base table sales, I will then try a fast refresh of the MV1:

SQL> set timing off
SQL> update sales set amount_sold=amount_sold*1 where rownum<2;

1 row updated.

SQL> commit;

Commit complete.

SQL> select mview_name,staleness from user_mviews;

MVIEW_NAME                     STALENESS
------------------------------ -------------------
MV1                            NEEDS_COMPILE

SQL> set timing on
SQL> exec dbms_mview.refresh('MV1','F')

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.62

It worked! But do I always have to refresh manually? No, we can also schedule a regular refresh like this:

SQL>  alter materialized view mv1 refresh start with sysdate next sysdate + interval '1' minute;

Materialized view altered.

SQL> alter session set nls_date_format='hh24:mi:ss';

Session altered.

SQL> select MVIEW_NAME,LAST_REFRESH_TYPE,LAST_REFRESH_DATE from user_mviews;

MVIEW_NAME                     LAST_REF LAST_REF
------------------------------ -------- --------
MV1                            FAST     10:40:05

SQL> select MVIEW_NAME,LAST_REFRESH_TYPE,LAST_REFRESH_DATE from user_mviews;

MVIEW_NAME                     LAST_REF LAST_REF
------------------------------ -------- --------
MV1                            FAST     10:41:04

Of course, a one minute refresh interval is extraordinary short. An other possibility to refresh the MVs very comfortably is on commit:

CREATE MATERIALIZED VIEW ADAM.MV1
 REFRESH FAST on commit 
 WITH ROWID
 enable QUERY REWRITE AS
 SELECT ADAM.SALES.CHANNEL_ID C1,
 SUM("ADAM"."SALES"."AMOUNT_SOLD") M1,
 COUNT("ADAM"."SALES"."AMOUNT_SOLD") M2,
 COUNT(*) M3
 FROM ADAM.SALES
 GROUP BY ADAM.SALES.CHANNEL_ID;

Materialized view created.

SQL> select MVIEW_NAME,LAST_REFRESH_TYPE,LAST_REFRESH_DATE from user_mviews;

MVIEW_NAME                     LAST_REF LAST_REF
------------------------------ -------- --------
MV1                            COMPLETE 11:16:28

SQL> update sales set amount_sold=amount_sold*1 where rownum<2;

1 row updated.

SQL> commit;

Commit complete.

SQL> select MVIEW_NAME,LAST_REFRESH_TYPE,LAST_REFRESH_DATE from user_mviews;

MVIEW_NAME                     LAST_REF LAST_REF
------------------------------ -------- --------
MV1                            FAST     11:19:56

But this may (although done in asynchronous fashion) slow down the DML on the base table, so it requires testing whether the slow down is noticeable or neglectable. So far  for now about Materialized Views. There is of course much more to say, but I think that this is enough for a brief introduction of that theme.

  1. #1 by moses on July 9, 2009 - 23:36

    great teaching.

  2. #2 by arshad on September 9, 2009 - 06:37

    Excellent step by step explanation!!! I exactly understand the purpose and use of materialized views now.

    Thanks
    Arshad

  3. #3 by Arun on October 7, 2009 - 12:09

    Really Useful Stuff.
    Explained very Clearly with Examples..

    Great Job Man..
    Keep try to post Useful Stuffs.

  4. #4 by Ramesh Babu on February 11, 2010 - 21:27

    Very good explanation , helped me in understanding the materialized view refresh concepts easily.

    Thanks
    Ramesh

  5. #5 by Uwe Hesse on February 12, 2010 - 09:19

    Thank you guys for your positive feedback! It’s what keeps Bloggers blogging, I think :-)

  6. #6 by Tamas Kocsis on March 2, 2010 - 23:22

    I’m doing Oracle trainings nowadays and the teacher is good, the curriculum is good, but I wish it all would be so clear and distinct as your explanation and examples.
    + 1 subscription :)

    Thank You
    Tamas

  7. #7 by Uwe Hesse on March 3, 2010 - 08:39

    Tamas,
    thanks for the compliment! And I wish you a nice & rewarding learning experience. Hope you take a course from Oracle University :-)

  8. #8 by Mili on July 29, 2010 - 07:26

    Really nice …can u provide me the link on which u publish articles/tutorials.

  9. #9 by Muhammad Adnan on December 22, 2010 - 21:01

    Great method of teaching.

  10. #10 by Murali M on January 5, 2011 - 03:55

    Thanks, really a nice explanation.

    -Murali*

  11. #11 by Uwe Hesse on January 5, 2011 - 17:55

    You’re welcome :-)

  12. #12 by deepssharma on January 14, 2011 - 11:57

    really good one :)
    when i started searching the material for materialized views I read the reasons why materialized views are used as follows:
    • Ease Network Loads
    • Create a Mass Deployment Environment
    • Enable Data Subsetting
    • Enable Disconnected Computing

    but they speed up the queries…I came to know from your article only.
    Thanks!

  13. #13 by Vikas on March 13, 2011 - 03:12

    Nice article. Thoroughly explained with examples. Thanks for publishing this article.

  14. #14 by Uwe Hesse on March 14, 2011 - 16:49

    Vikas,
    you’re welcome :-)
    Thanks for the nice feedback!

  15. #15 by Bora Yüret on April 1, 2011 - 08:38

    A very good teaching style, thanks for the tutorial.

    Bora Yüret

  16. #16 by Uwe Hesse on April 1, 2011 - 08:54

    Bora,
    you’re welcome :-)

  17. #17 by Sridhar on May 17, 2011 - 12:35

    Hi,

    Explanation was really Excellent
    Can please give me some examples on how to implement Application Contexts in Oracle.

    thanks,
    Sridhar

  18. #18 by Uwe Hesse on May 18, 2011 - 13:08

    Sridhar,
    thank you for the positive feedback! I have no plans to cover Application Contexts for now, though. The topic seems even a little boring to me, sorry :-)

  19. #19 by Md. Rajibul Islam on May 25, 2011 - 13:09

    Your teaching is really helpful for me and I understand the topic clearly. Many many thanks to you.

  20. #20 by Uwe Hesse on May 26, 2011 - 08:10

    You’re welcome, I am happy if you find it helpful :-)

  21. #21 by Krishna Kumar on May 27, 2011 - 22:56

    Awesome tutorial. Just Googled to know the definition difference between views and materialized view… But came to know more stuffs from your blog.. Nice work.. Thank you…

  22. #22 by Uwe Hesse on May 30, 2011 - 08:51

    Thank you for the nice feedback, Krishna!

  23. #23 by Ezekiel Filane on June 2, 2011 - 11:47

    Excellent! 100%….ilike :-)

  24. #24 by Uwe Hesse on June 3, 2011 - 08:52

    Ezekiel, you’re welcome :-)

  25. #25 by totalz on July 4, 2011 - 10:06

    Is there a way to refresh on commit with “complex” query, like multiple tables in from clause? I have created MV logs on those master tables, but same ”
    ERROR at line 1:
    ORA-12015: cannot create a fast refresh materialized view from a complex query

  26. #26 by Uwe Hesse on July 4, 2011 - 16:33

    Unfortunately, it is not always possible to create fast refreshable MVs – it depends on your Database version, though: 11g may be able to support fast refresh where 10g doesn’t.

    It is often not suffcient to have MV Logs only. The MV query must be customized accordingly.
    Did you try DBMS_ADVISOR.TUNE_MVIEW already?

  27. #27 by Jeannie Brown on July 14, 2011 - 23:01

    If the table gets cleared that the materialized view is pointing to will it still have the data in the view as long as it hasn’t been refreshed?

  28. #28 by Uwe Hesse on July 15, 2011 - 08:32

    Yes, it will stay the same as before until the refresh.

  29. #29 by G SElvam on August 27, 2011 - 09:10

    Thanks for GOod explaination and reason to use M Views.

    One my clinet facining ora-03113 frequently…..what is reason on Oracle 11g – The same was working well on Oracle 10g

  30. #30 by Uwe Hesse on September 1, 2011 - 18:38

    Thank you for the nice words! I have no clue about the cause of your error, though. Suggest that you invoke Oracle Support for this kind of problems: They are very good with this and can even make a living from it :-)

    If you have no support contract, you may try
    http://forums.oracle.com/forums/forum.jspa?forumID=61&start=0

  31. #31 by neerajvasudeva on December 28, 2011 - 22:16

    Concept very nicely explained.

    However I have a very small doubt, let’s say after my user ‘X’ created MV, now as i understood from your blog post, if user ‘Y’ invokes query on source/base table , it would internally hit MV How can user ‘Y’ avoid hitting internally MV , can i assume once we remove “query rewrite” clause which was used during creation of MV it would not hit MV even if someone is just querying on base / source table ??

    Will try it out once i have a demo system, but appreciate if u can clear my doubt in the meantime.

  32. #32 by Uwe Hesse on December 29, 2011 - 15:13

    Generally, there should be no reason why user Y will not want to benefit from the speedup, accompanied by the internal rewrite on the MV.

    If for any reason, the rewrite is not desired, you can switch it off on the session level (alter session set query_rewrite_enabled=false;) or on the statement level with a hint (/*+ NO_REWRITE */)

    There should be no need to change the rewrite clause of the MV, since that would disable the rewrite for every session.

  33. #33 by C Franklin on January 11, 2012 - 16:57

    We are using MVs to move data from one schema (owned by another group) to our schema to minimize the need for constantly hitting their data. We now would like to have the ability to backup the prod MVs and move them down to a DEVL/Test environment but this invalidates the MVs. Is there a way just to backup the PROD MV tables (not the definitions) and move them down and have the ability to write to them (to get rid of personal data)? Otherwise we have to backup the entire schema owned by the other group and rerun all ETL in the lower environment. Your help is appreciated.

  34. #34 by C Franklin on January 11, 2012 - 16:58

    Sorry… didn’t press “Notify me…”

  35. #35 by Uwe Hesse on January 12, 2012 - 12:18

    C Franklin,
    I am not sure I understood your issue. Why not just do a create table as select * from MV for the desired schema?

  36. #36 by C Franklin on January 13, 2012 - 17:18

    That’s what we are going to have the DBAs perform for us. But this means the lower environments won’t really be like production. So if the SysTest or the UAT testers need to test ETL they can’t because the MVs are now tables and our MV refresh code won’t work. If we CTAS/backup from the other schemas’ prod into the lower environment matching schema to perform ETL from there, then we have issues because we share the lower environments with the other groups too and we can corrupt each others testing. Was hoping there was a way to backup the MVs and plop them into the lower environments without corrupting them, but so far we haven’t found such a way. Also, we are unable to update the records in the MVs to mask the personal data. MVs are cool, but have issues. Thanks for you help…

  37. #37 by vijaymukthavijay on February 27, 2012 - 06:54

    SIr,
    This is very very clear in understanding.
    The demonstration is very easier to understand.
    Thank you very much for helping us in gaining the knowledge..

  38. #38 by Ajinkya K. Soitkar on April 18, 2012 - 16:53

    Hello Uwe! It’s a great note! Thanks for a detailed explanation. I just had a query,
    Are mview logs dropped from the mview log table once they are used for fast refresh? Also, if the mview log size is very high for e.g. 2-3 GB’s does it mean it mview has stale data? Or the Mviews are not being used at all?
    Would appreciate your inputs here. Thanks!
    Ajinkya S, DBA

  39. #39 by Dharmendra on April 19, 2012 - 07:57

    Thanks
    I learn lot of things

  40. #40 by Dharmendra on April 19, 2012 - 07:58

    Can we refresh our mv using trigger

  41. #41 by Uwe Hesse on April 19, 2012 - 08:28

    Ajinkya, thank YOU for the feedback :-) To your questions: MV logs are not dropped after a refresh, but their rows get removed when all dependent MVs have been refreshed. Very large MV logs that never get their rows deleted indicate that no (fast) refresh has been done for all dependent MVs for a long time. An easy way to see whether your MVs are beneficial at all for your workload is the SQL Access Advisor.

  42. #42 by Uwe Hesse on April 19, 2012 - 08:30

    Dharmendra, you’re welcome. Instead of using a trigger on the base table, better use refresh on commit. It basically does the same but better :-)

  43. #43 by yugen on May 7, 2012 - 06:29

    great one sir

  1. Blogroll Report 03/07/2009 – 10/07/2009 « Coskan’s Approach to Oracle
  2. Posting about Materialized Views got published in OU EMEA Newsletter! « The Oracle Instructor
  3. Merry Christmas and a Happy New Year! « The Oracle Instructor
  4. Materialized Views Introduction « Little Bit Technical…!!!
  5. Merry Christmas & A Happy New Year 2012! « The Oracle Instructor
  6. Merry Christmas & A Happy New Year 2012! | Oracle Administrators Blog - by Aman Sood
  7. Oracle Technology Software Index
  8. Materialized Views & Partition Change Tracking « The Oracle Instructor

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 635 other followers