Why does a ORA-12054 error occur when creating this simple materialized view example? - oracle

ALTER TABLE RECORDINGS ADD PRIMARY KEY (ID);
CREATE MATERIALIZED VIEW LOG ON RECORDINGS TABLESPACE USERS NOLOGGING;
DROP MATERIALIZED VIEW REC_SEARCH_TEST;
CREATE MATERIALIZED VIEW REC_SEARCH_TEST
REFRESH COMPLETE ON COMMIT
AS (
SELECT DISTINCT ID, TITLE FROM RECORDINGS
);
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view
Cannot understand what is wrong here, I know that if I take out the DISTINCT clause it works, but why can I not use 'DISTINCT' if I specify 'REFRESH COMPLETE ON COMMIT' which is required.
If I use DISTINCT and REFRESH on demand there is no problem, but these are not the requirements.

Seems like with the addition of the DISTINCT, you've made your view's underlying SQL ineligible for fast refresh, and therefore not able to be used with ON COMMIT (even tho you specify refresh complete instead of refresh fast). From Oracle docs:
The two refresh execution modes are ON COMMIT and ON DEMAND. Depending
on the materialized view you create, some of the options may not be
available. Table 8-4 describes the refresh modes.
Table 8-4 Refresh Modes
ON COMMIT
Refresh occurs automatically when a transaction that modified one of
the materialized view's detail tables commits. This can be specified
as long as the materialized view is fast refreshable (in other words,
not complex). The ON COMMIT privilege is necessary to use this mode.
ON DEMAND
Refresh occurs when a user manually executes one of the available
refresh procedures contained in the DBMS_MVIEW package (REFRESH,
REFRESH_ALL_MVIEWS, REFRESH_DEPENDENT).
The same document link has a list of restrictions for fast refresh as well.

"Perhaps the example isn't the best, because I want to expand the view
to a more complicated query that will require a distinct keyword,
right now I am just trying to get it working on a basic level. "
The DISTINCT is the cause of the ORA-12054.
SQL> CREATE MATERIALIZED VIEW REC_SEARCH_TEST
REFRESH COMPLETE ON COMMIT
AS (
SELECT DISTINCT empno, ename FROM emp
)
/
2 3 4 5 6
SELECT DISTINCT empno, ename FROM emp
*
ERROR at line 4:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view
Elapsed: 00:00:01.14
SQL> SQL>
SQL> CREATE MATERIALIZED VIEW REC_SEARCH_TEST
REFRESH COMPLETE ON COMMIT
AS (
SELECT empno, ename FROM emp
)
/
2 3 4 5 6
Materialized view created.
Elapsed: 00:00:02.33
SQL>
Why not start with a something that works? Remove the DISTINCT. Get your MView working. Then complicate it later when it becomes necessary.
Although, as you already know you cannot use a DISTINCT you will have to revise either your query's logic or your refresh strategy.

The important thing to note about the question is that it is not about fast refresh, but complete refresh. Thus, there is no logical reason that the usual restrictions for fast refresh on commit should apply, except the one that all referenced objects must be local.
The "refresh complete on commit is a relatively new feature, so the best answer to the "why" question is probably "Oracle has not yet implemented this fully, please check future versions of Oracle Database". Not very useful, but true...

Related

Can Materialized View fast refresh work when based on views are based on tables?

I am reviewing my team's database setup, particularly focusing on Materialized Views. In most cases, we are currently doing 'Complete' refreshes, and I want to move to doing fast refreshes.
In some cases, this is straight forward -- the MV is based directly on a table on our source database, and I can enable MVIEW LOGS on the table and recreate the MV.
But in a number of cases, the MVs are based on a combination of other MVs, and Views, etc, that go several levels deep before I get to the tables on our source database.
In these cases, if I track down the ultimate source tables, will enabling MVIEW LOGS on them allow the top MV and any intermediate MVs, to use fast refresh?
The Oracle documentation contains an example for a FAST REFRESH of a materialized view based on an UNION ALL view:
CREATE VIEW view_with_unionall AS
(SELECT c.rowid crid, c.cust_id, 2 umarker
FROM customers c WHERE c.cust_last_name = 'Smith'
UNION ALL
SELECT c.rowid crid, c.cust_id, 3 umarker
FROM customers c WHERE c.cust_last_name = 'Jones');
CREATE MATERIALIZED VIEW unionall_inside_view_mv
REFRESH FAST ON DEMAND AS
SELECT * FROM view_with_unionall;
So in principle, you can indeed fast refresh materialized views based on views.
Some things to note:
there are a couple of restrictions for fast refreshable materialized views. E.g. you cannot use ROWNUM, SYSDATE or HAVING. See the docs for details
somewhat counterintuitively, a FAST REFRESH is not always faster than a COMPLETE REFRESH. This depends on the amount of data that has changed since the last refresh; IMHO, Oracle should have used the term INCREMENTAL REFRESH instead
Oracle provides a procedure for that: DBMS_MVIEW.EXPLAIN_MVIEW
You can use this procedure to check whether your Materialized Views is capable for FAST REFRESH, it also tells you the reason why it is not.
For me the most strange restriction for FAST REFRESH is: When you join several tables you have to use the (old) Oracle Join syntax, ANSI join syntax does not work. Some time ago a created a case at Oracle support for this issue, however the answer from Oracle was: "This is not a bug, it is just a lack of documentation."(!)
I don't know if it still applies for Oracle 12c version.

Oracle materialized view computational cost

Is the computational cost of updating a stored procedure materialized view, in Oracle, based on the query execution or the result set? More specifically, does Oracle store the results of the query in such a way that contributes significantly to the time required to refresh the view?
Of course, queries which take very long to execute as well as incredibly large or small result sets make this impossible to answer ubiquitously.
The question is more about how the view actually stores the result set (in memory, on disk) so I can think about how frequently to rebuild materialized views.
Materialized view is basically a table combined with an algorithm to update it.
01:37:23 HR#sandbox> create materialized view mv_dual as select dummy from dual;
Materialized view created.
Elapsed: 00:00:00.52
01:37:56 HR#sandbox> select object_name, object_type from user_objects where object_name = 'MV_DUAL';
OBJECT_NAME OBJECT_TYPE
--------------- -------------------
MV_DUAL TABLE
MV_DUAL MATERIALIZED VIEW
Elapsed: 00:00:00.01
You can also create materialized views on prebuilt tables.
If we talk about refresh - there are two options: fast refresh and complete refresh.
Complete refresh just re-executes MV query, while fast refresh performs incremental updates.
http://docs.oracle.com/cd/E16338_01/server.112/e10706/repmview.htm#i29858
there are two types of mviews
Complete refresh mview - the entier mview will be rebuild every refresh. similar to delete and insert (notice: if you specify atomic = F or have version < 9 it will be truncate / insert append).
Fast refresh mview - oracle will create a table to store incremental changes. when refreshing, the changes stored in the side table will be applied to the mview.
fast refresh is faster on refresh but slows down dml operations on the base table.
when you consider your refresh strategy you should consider how much changes are applied to the base table and how often you need to refresh the mview.

Refreshing Materialzed View in Oracle

I have created materialized view (MV) on a table which is updated once in a month..
Do we have any automatic way to refresh my MV...
I mean how refresh of MV is done is it manual or
Automatic how frequent we can do????
like can i use Trigger for it..??
I am using Oracle9i on PL/SQL developer
Thanks
You can refresh the view manually if you want
execute DBMS_SNAPSHOT.REFRESH( 'MAT_VIEW','OPTION');
Where the OPTION parameter could be
F, f Fast Refresh
C, c Complete Refresh
A Always perform complete refresh
? Use the default option
The automatic refresh rate are supplied when you create the materialized view
CREATE MATERIALIZED VIEW MAT_VIEW
REFRESH FAST START WITH SYSDATE
NEXT SYSDATE + TIME_INTERVAL --
WITH PRIMARY KEY
AS SELECT * FROM TABLE;
Remember to create a log for the table if FAST START are used.
I recomend you the following link about Materialized View. It has a lot of info about this topic: http://www.dba-oracle.com/art_9i_mv.htm

Oracle materialized view question

I have a table that holds information about different events, for example
CREATE TABLE events (
id int not null primary key,
event_date date, ...
)
I realized that 90% of all queries access only today events; the older rows are stored for history and eventually moved to an archive table.
However, events table is still large, and I wonder if I can improve the performance by creating a materialized view that has something like WHERE event_date = trunc(sysdate) and maybe index on event_date ? Is it allowed at all?
Thanks
yes this is allowed see "primary key materialized view":
Primary key materialized views may contain a subquery so that you can
create a subset of rows at the remote materialized view site
and "complex materialized view"
If you refresh rarely and want faster query performance, then use
Method A (complex materialized view).
If you refresh regularly and can sacrifice query performance, then use Method B (simple materialized view).
at http://download.oracle.com/docs/cd/B10500_01/server.920/a96567/repmview.htm
In your example chances are good IMHO that this is not a "complex materialized view":
CREATE MATERIALIZED VIEW events_today REFRESH FAST AS
SELECT * FROM EVENT WHERE event_date = trunc(sysdate);
Just try it and see if Oracle accepts it with the REFRESH FAST clause.
EDIT - another option:
Depending on your DB Edition (Enterprise + Partitioning) and Version (11gR2) you could use a new Oracle feature called INTERVAL partitioning to define "daily partitions" within the existing table. This way most of your queries get alot faster without effectively duplicating the data - see http://www.oracle.com/technetwork/database/options/partitioning/twp-partitioning-11gr2-2009-09-130569.pdf

Update materialized view when urderlying tables change

I have a materialized view defined this way:
CREATE MATERIALIZED VIEW M_FOO
REFRESH COMPLETE ON COMMIT
AS
SELECT FOO_ID, BAR
FROM FOO
WHERE BAR IS NOT NULL
GROUP BY FOO_ID, BAR
/
COMMENT ON MATERIALIZED VIEW M_FOO IS 'Foo-Bar pairs';
I wrote as a sort of cache: the source table is huge but the number of different pairs is fairly small. I need those pairs to get them JOINed with other tables. So far so good: it absolutely speeds queries.
But I want to make sure that the view does not contain obsolete data. The underlying table is modified four or five times per month but I don't necessarily know when. I understand that a materialized view can be defined so it updates when the source tables change. However, the docs get pretty complicate.
What's the exact syntax I need to
use?
Do I need to create a materialized
view log?
What's the difference between fast
and complete refresh?
To take your questions in reverse order
A FAST refresh is also known as an incremental refresh. That should give you a clue as to the difference. A COMPLETE refresh rebuilds the entire MVIEW from scratch, whereas a FAST refresh applies just the changes from DML executed against the feeder table(s).
In order to do execute FAST refreshes you need the appropriate MVIEW LOG. This tracks changes to the data of the underlying tables, which allows Oracle to efficiently apply a delta to the materialized view, rather than querying the whole table.
As for the syntax, here are the basics:
SQL> create materialized view log on emp
2 with rowid, primary key, sequence (deptno, job)
3 including new values
4 /
Materialized view log created.
SQL> create materialized view emp_mv
2 refresh fast on commit
3 as
4 select deptno, job from emp
5 group by deptno, job
6 /
Materialized view created.
SQL>
The ON COMMIT clause means that the MVIEW is refreshed transactionally (as opposed to ON DEMAND which is regular refresh in bulk). The REFRESH clauses specifies whether to apply incremental or complete refreshes. There are some categories of query which force the use of COMPLETE refresh, although these seem to diminish with each new version of Oracle.
A quick test to see that it works ...
SQL> select * from emp_mv
2 order by deptno, job
3 /
DEPTNO JOB
---------- ---------
10 MANAGER
10 PRESIDENT
10 SALES
20 ANALYST
20 CLERK
20 MANAGER
30 CLERK
30 MANAGER
30 SALESMAN
40 CLERK
40 DOGSBODY
11 rows selected.
SQL>
How about a new record?
SQL> insert into emp (empno, ename, deptno, job)
2 values (6666, 'GADGET', 40, 'INSPECTOR')
3 /
1 row created.
SQL> commit
2 /
Commit complete.
SQL> select * from emp_mv
2 order by deptno, job
3 /
DEPTNO JOB
---------- ---------
10 MANAGER
10 PRESIDENT
10 SALES
20 ANALYST
20 CLERK
20 MANAGER
30 CLERK
30 MANAGER
30 SALESMAN
40 CLERK
40 DOGSBODY
40 INSPECTOR
12 rows selected.
SQL>
You can find more details on the syntax in the SQL Reference. It's also worth reading the Materialized View chapter in the Data Warehousing Guide.
Despite the concerns of the commenters below this does work as advertised. Unfortunately the usual places for publishing demos (SQL Fiddle, db<>fiddle) do not allow materialized views. I have published something on Oracle SQL Live (free Oracle account required): I am awaiting Oracle approval for it and will update this question when it arrives.
A fast refresh will only insert/update/delete changed data into the materialized view. A complete refresh will empty the materialized view and then copy over all rows.
The "on commit" means the materialized view will be refreshed whenever a change is committed in the master table. So your current syntax is going to be extremely inefficient. Every time somebody changes any row in foo, m_foo will be truncated and then every row in foo table will be inserted.
You can do better with fast refreshes, where only the modified rows in foo will be sent to m_foo. That gives you consistency without lots of overhead.
create materialized view log on foo with primary key; -- assuming you have a primary key, you should
create materialized view m_foo refresh fast on commit as \;
There are some additional subtleties with grants and synonyms if you're using db links, or the schema that owns foo isn't the one that owns m_foo.

Resources