Error creating a materialized view in Oracle - sql

I'm trying to build the following MV:
CREATE MATERIALIZED VIEW LOG ON EVENTS
WITH ROWID, SEQUENCE(AIRCRAFT, MONTHS_YEARS) INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG ON MANTEINANCE
WITH ROWID, SEQUENCE(AIRCRAFT, MONTHS_YEARS) INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW MV_LOGBOOK
BUILD IMMEDIATE
REFRESH FAST START WITH (SYSDATE) NEXT (add_months(trunc(sysdate,'mm'),1)) WITH ROWID
ON DEMAND
DISABLE QUERY REWRITE AS
SELECT * FROM EVENTS e, MANTEINANCE m
WHERE e.AIRCRAFT = m.AIRCRAFT AND
e.MONTHS_YEARS = m.MONTHS_YEARS;
I created the logs associated with each table, also including the values inside the where clause, however, it gives me the error:
SQL Error [12052] [99999]: ORA-12052: no se puede realizar un refrescamiento rĂ¡pido de la vista materializada MV_LOGBOOK
And I'm unsure why this is happening. Also, I would like to ask why I need a ROWID if there is already a PK.
Thanks.

An MV with joins must explicitly include the ROWID from each source table in the MV columns. You should also explicitly name every column and not use implicit selects, and use column aliases to differentiate columns with the same name because just like any table or view you can't have multiple columns with the same name:
CREATE MATERIALIZED VIEW MV_LOGBOOK
BUILD IMMEDIATE
REFRESH FAST START WITH (SYSDATE) NEXT (add_months(trunc(sysdate,'mm'),1)) WITH ROWID
ON DEMAND
DISABLE QUERY REWRITE AS
SELECT e.rowid e_rowid,
m.rowid m_rowid,
e.col1 e_col1,
e.col2 e_col2,
m.col1 m_col1,
m.col2 m_col2
FROM EVENTS e, MANTEINANCE m
WHERE e.AIRCRAFT = m.AIRCRAFT AND
e.MONTHS_YEARS = m.MONTHS_YEARS;

Related

Snowflake SQL Compilation Error: View Definition Declared but view Query Produced

I've just gotten a new query error that I haven't changed anything to. Any advice on what to do? Thanks
SQL compilation error:
View definition for '**********' declared 115 column(s), but view query produces 117 column(s).
This is speculation, but it sounds like your view is using select x.*, where * means to get all the columns from some table.
Then, the underlying table changes . . . and voila, you might have a problem.
I've just gotten a new query error that I haven't changed anything to. Any advice on what to do?
If the query started to produce errors it means that the defintion of view is no longer "valid/up-to-date". Most likely the underlying table has been altered.
CREATE VIEW
View definitions are not dynamic. A view is not automatically updated if the underlying sources are modified such that they no longer match the view definition, particularly when columns are dropped. For example:
A view is created referencing a specific column in a source table and the column is subsequently dropped from the table.
A view is created using SELECT * from a table and any column is subsequently dropped from the table.
In either of these scenarios, querying the view returns a column mismatch error.
Steps to reproduce the scenario:
CREATE OR REPLACE TABLE t(col1 INT, col2 INT);
INSERT INTO t(col1, col2) VALUES (1,1);
CREATE OR REPLACE VIEW v_t AS SELECT * FROM t;
SELECT * FROM v_t;
--COL1 COL2
--1 1
So far so good. Now altering the underlying table by adding new column:
ALTER TABLE t ADD COLUMN col3 INT DEFAULT 3;
SELECT * FROM v_t;
SQL compilation error: View definition for 'V_T' declared 2 column(s), but view query produces 3 column(s).
Recreation of the view and keeping its definition on par with underlying tables should resolve it:
CREATE OR REPLACE VIEW v_t
COPY GRANTS
AS
SELECT * FROM t;
-- using * will work to refresh it but I would not recommend it
-- and explicitly describe columns instead
CREATE OR REPLACE VIEW v_t
COPY GRANTS -- to preserve already granted permissions
AS
SELECT col1, col2, col3 FROM t;
SELECT * FROM v_t;
-- COL1 COL2 COL3
-- 1 1 3
I found myself with a similar issue this morning. I had copied my query from a txt file I had and pasted it into a worksheet and tired to run it and got the same error. I had used the Table with the definition issue for a join and only for 1 column so I didn't see why it was giving me such an error.
All a look of check tables I commented on the Worksheet the error I was seeing. But I decided to run it again and it worked.
This tells me that Snowflake was using what it had cached but after editing the query it saw it as a new query and re-ran it instead of erroring out when what is in the cache doesn't match the definition.

Materialized View by Joining View and Tables

Can we create Materialized view by joining already existing view(normal)?. My requirement is to create a MVIEW on tables(A,B) and View(c). Is it possible? If yes, do we have see any performance issue or refresh issue.
This need to done on Oracle DB.
CREATE MATERIALIZED VIEW EMP_MVIEW refresh force ON COMMIT
select
EMP.ID, EMP.NAME, DV.*
EMP_TABLE EMP
LEFT OUTER JOIN DETAILS_VIEW dv ON DV.EMP_ID=EMP.EMP_ID
First thing first, Unfortunately ANSI join syntax is not allowed for Oracle Materialized view, use the old Oracle join syntax. It is a bug in Oracle i believe so
Anything I am going to demonstrate with my answer is only regarding Materialized with FAST REFRESH option
Coming back to your original question whether we can use a normal view inside materialized view with incremental refresh capability:
Answer is No
Having said that, if we try will end up with error and unable to create it as I will demonstrate below,
Table Structure: (used only for demo as example and not actual normalized tables
CREATE TABLE emp(emp_id NUMBER primary key
,empname VARCHAR2(1000));
CREATE TABLE address_details(address_id NUMBER primary key
,address_text VARCHAR2(1000)
,emp_id NUMBER);
CREATE TABLE salary_details(sal_id NUMBER primary key
,salary NUMBER
,emp_id NUMBER);
Materialized view Logs:
--drop statements
DROP MATERIALIZED VIEW LOG ON emp;
DROP MATERIALIZED VIEW LOG ON address_details;
DROP MATERIALIZED VIEW LOG ON salary_details;
--create statements
--default
CREATE MATERIALIZED VIEW LOG ON emp;
CREATE MATERIALIZED VIEW LOG ON address_details;
CREATE MATERIALIZED VIEW LOG ON salary_details;
--with primary key (same as default above but I would stick to mention it explicitly for understanding and versioning (svn or git) purpose
CREATE MATERIALIZED VIEW LOG ON emp WITH PRIMARY KEY;
CREATE MATERIALIZED VIEW LOG ON address_details WITH PRIMARY KEY;
CREATE MATERIALIZED VIEW LOG ON salary_details WITH PRIMARY KEY;
--with primary key and rowid
CREATE MATERIALIZED VIEW log ON emp WITH PRIMARY KEY, ROWID;
CREATE MATERIALIZED VIEW log ON address_details WITH PRIMARY KEY, ROWID;
CREATE MATERIALIZED VIEW log ON salary_details WITH PRIMARY KEY, ROWID;
1. First try creating MV with normal view as you want:
CREATE OR REPLACE VIEW DETAILS_VIEW AS
SELECT sal_id
,salary
,address_id
,address_text
,sl.emp_id
--,sl.rowid sl_rowid
--,ad.rowid ad_rowid
FROM salary_details sl
,address_details ad
WHERE sl.emp_id = ad.emp_id;
DROP MATERIALIZED VIEW emp_mview;
CREATE MATERIALIZED VIEW EMP_MVIEW
REFRESH FORCE ON COMMIT
AS
SELECT emp.emp_id
,emp.empname
,dv.sal_id
,dv.salary
,dv.address_id
,dv.address_text
--,emp.rowid emp_rowid
--,dv.sl_rowid
--,dv.ad_rowid
FROM emp emp
,details_view dv
WHERE emp.emp_id = dv.emp_id(+);
Result:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view
Believe me I have tried with all scenarios creating log with primary key and rowid and adding the rowid to the select clause and nothing works
Moving forward:
2. Second try creating MV with standard joins from oracle by putting all individual base tables in the from clause:
I have created the materialized view logs with option primary key as I mentioned in Materialized view Logs section above.
CREATE MATERIALIZED VIEW EMP_MVIEW
REFRESH FORCE ON COMMIT
AS
SELECT emp.emp_id
,emp.empname
,sl.sal_id
,sl.salary
,ad.address_id
,ad.address_text
FROM emp emp
,salary_details sl
,address_details ad
WHERE emp.emp_id = sl.emp_id(+)
AND emp.emp_id = ad.emp_id(+);
BINGO: Materialized view created ,
But, wait wait... Does it mean now I can do a FAST REFRESH ? Lets check:
We can Analyzing Materialized View Capabilities using DBMS_MVIEW.EXPLAIN_MVIEW which will insert capabilities details to table called MV_CAPABILITIES_TABLE (available with # $ORACLE_HOME/rdbms/admin/utlxmv.sql). If we don't have the script and grants from DBA you need to get it. However I will anyhow give the scripts below as well
HOW TO ANALYSE CAPABILITY OF MV:
--table structure
CREATE TABLE mv_capabilities_table (
statement_id VARCHAR2(30),
mvowner VARCHAR2(30),
mvname VARCHAR2(30),
capability_name VARCHAR2(30),
possible CHAR(1),
related_text VARCHAR2(2000),
related_num NUMBER,
msgno INTEGER,
msgtxt VARCHAR2(2000),
seq NUMBER
);
--delete always before analyzing for a view to have only rows for a specific and not to have where clause to filter :)
DELETE FROM mv_capabilities_table;
--run this script which will analyze and insert into mv_capabilities_table
BEGIN
dbms_mview.explain_mview('EMP_MVIEW');
END;
/
/***ANALYSIS RESULT:***/
--I am intersted only with data related to FAST REFRESH category
SELECT capability_name
,possible
,substr(msgtxt
,1
,60) AS msgtxt
FROM mv_capabilities_table
WHERE capability_name LIKE '%FAST%';
/**
CAPABILITY_NAME POSSIBLE MSGTXT
REFRESH_FAST N
REFRESH_FAST_AFTER_INSERT N the SELECT list does not have the rowids of all the detail t
REFRESH_FAST_AFTER_INSERT N mv log must have ROWID
REFRESH_FAST_AFTER_INSERT N mv log must have ROWID
REFRESH_FAST_AFTER_INSERT N mv log must have ROWID
REFRESH_FAST_AFTER_ONETAB_DML N see the reason why REFRESH_FAST_AFTER_INSERT is disabled
REFRESH_FAST_AFTER_ANY_DML N see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled
REFRESH_FAST_PCT N PCT FAST REFRESH is not possible if query contains LEFT OUTE
**/
If you look at the result with text **mv log must have ROWID**Above result of analysis tells us the materialized view logs are missing the ROWID option and thus FAST REFRESH is not possible.
Note: Other columns in mv_capabilities_table will also tell you the exact tables which i have not included and you can check and test by yourself to see.
Moving forward..
3. Third try creating MV with standard joins from oracle and MV Logs with option ROWID included:
steps: (will not provide the scripts again but just providing the steps to simulate
I will drop and re-create the MV logs with option ROWID as mentioned in Materialized view Logs section above.
Then I will drop and re-create the same MV definition I used in my 2nd try which will be eventually created
Next I will try to repeat the steps to analyze the MV as described in HOW TO ANALYSE CAPABILITY OF MV section
What do i get in my analysis report:
/**
CAPABILITY_NAME POSSIBLE MSGTXT
REFRESH_FAST N
REFRESH_FAST_AFTER_INSERT N the SELECT list does not have the rowids of all the detail t
REFRESH_FAST_AFTER_ONETAB_DML N see the reason why REFRESH_FAST_AFTER_INSERT is disabled
REFRESH_FAST_AFTER_ANY_DML N see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled
REFRESH_FAST_PCT N PCT FAST REFRESH is not possible if query contains LEFT OUTE
**/
Ufffff: I am tired actually and again the MV is not having the FAST REFRESH capable yet and the reason tells us "the SELECT list does not have the rowids of all the detail tables"
What it means: the next criteria for FAST REFRESH is Rowids of all the tables in the FROM list must appear in the SELECT list of the query
So,
4. Fourth and last try creating MV with standard joins from oracle and MV Logs with option ROWID included and also the details tables rowids are now included in the select clause:
steps:
As with 3rd try the MV logs required for first refresh are in place I will drop and re-create the MV again but this time with adding the rowids of detail table.
MV Final Script:
CREATE MATERIALIZED VIEW EMP_MVIEW
REFRESH FORCE ON COMMIT
AS
SELECT emp.emp_id
,emp.empname
,sl.sal_id
,sl.salary
,ad.address_id
,ad.address_text
,emp.rowid emp_rowid
,sl.rowid sl_rowid
,ad.rowid ad_rowid
FROM emp emp
,salary_details sl
,address_details ad
WHERE emp.emp_id = sl.emp_id(+)
AND emp.emp_id = ad.emp_id(+);
Now as the MV created , lets analyse the capabilities of the MV as described in HOW TO ANALYSE CAPABILITY OF MV section one more time. (fingers crossed)
Result:
SELECT capability_name
,possible
,substr(msgtxt,1,60) AS msgtxt
FROM mv_capabilities_table
WHERE capability_name LIKE '%FAST%';
/**
CAPABILITY_NAME POSSIBLE MSGTXT
REFRESH_FAST Y
REFRESH_FAST_AFTER_INSERT Y
REFRESH_FAST_AFTER_ONETAB_DML Y
REFRESH_FAST_AFTER_ANY_DML Y
REFRESH_FAST_PCT N PCT FAST REFRESH is not possible if query contains LEFT OUTE
**/
Finally the REFRESH_FAST capability is possible as we can see POSSIBLE -> Y
Sorry for long answer but I thought I should put how I learn the things about in MV in past which could be useful to share.
Some Links I found always useful with respect to Oracle materialized view:
Official site from oracle
MV with Warehouse Oracle Official site
Oracle base
My Favorite from Alberto Dell'Era
One good SO answer about MV
Cheers!!

ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

I am facing a problem while creating a materialized view for our aggregations. If DML occurs on the source table, the materialized view should refresh automatically and show the updated results. The idea is to store the aggregation results and just fetch the numbers directly rather than doing a query, and I basically want to see if this meets our objective. Our table is going to have up to 3 million inserts a day.
As per the link Example 8-3 Example 3: Creating a Materialized View I did:
CREATE MATERIALIZED VIEW LOG ON table WITH SEQUENCE, ROWID
(SUBJECTID)
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW ih_data_aggregated_view
PARALLEL
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
AS
SELECT SUBJECTID ,count(*)as totalcount ,avg(price)as avgprice,sum(price) as totalprice
FROM table
WHERE SUBJECTID='xxxxx' GROUP by SUBJECTID;
but this gets:
java.sql.SQLException: SQLException: ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view
The documentation includes general restrictions on materialised view and on materialised view with aggregates.
While working through those lists is instructive, you can see whether an existing or potential materialised view can be fast-refreshed by examining the results fromthe dbms_mview.explain_mview procedure:
set serveroutput on
declare
msg_array SYS.ExplainMVArrayType;
begin
dbms_mview.explain_mview (q'[
SELECT SUBJECTID ,count(*)as totalcount ,avg(price)as avgprice,sum(price) as totalprice
FROM your_table
WHERE SUBJECTID='xxxxx'
GROUP by SUBJECTID
]',
msg_array);
for i in msg_array.first..msg_array.last loop
dbms_output.put_line(rpad(msg_array(i).capability_name, 30)
||' '|| msg_array(i).possible
||' '|| msg_array(i).msgtxt);
end loop;
end;
/
...
REFRESH_FAST F
...
REFRESH_FAST_AFTER_INSERT F agg(expr) requires correspondng COUNT(expr) function
REFRESH_FAST_AFTER_ONETAB_DML F SUM(expr) without COUNT(expr)
REFRESH_FAST_AFTER_ONETAB_DML F see the reason why REFRESH_FAST_AFTER_INSERT is disabled
REFRESH_FAST_AFTER_ANY_DML F see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled
REFRESH_FAST_PCT F PCT is not possible on any of the detail tables in the materialized view
...
As #vercelli mentioned, and as the REFRESH_FAST_AFTER_INSERT message suggests, you need to change count(*) to count(price). But that isn't the whole story; if you just change that you'll see:
REFRESH_FAST F
...
REFRESH_FAST_AFTER_INSERT F mv log does not have all necessary columns
REFRESH_FAST_AFTER_ONETAB_DML F see the reason why REFRESH_FAST_AFTER_INSERT is disabled
REFRESH_FAST_AFTER_ONETAB_DML F COUNT(*) is not present in the select list
REFRESH_FAST_AFTER_ANY_DML F see the reason why REFRESH_FAST_AFTER_ONETAB_DML is disabled
Your materialised view log has to include the column you are aggregating:
CREATE MATERIALIZED VIEW LOG ON your_table WITH SEQUENCE, ROWID
(SUBJECTID,PRICE)
INCLUDING NEW VALUES;
Materialized view LOG created.
CREATE MATERIALIZED VIEW ih_data_aggregated_view
PARALLEL
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
AS
SELECT SUBJECTID ,count(price)as totalcount ,avg(price)as avgprice,sum(price) as totalprice
FROM your_table
WHERE SUBJECTID='xxxxx'
GROUP by SUBJECTID;
Materialized view IH_DATA_AGGREGATED_VIEW created.
The missing count(*) is still reported but as this is against a single table it isn't preventing fast refresh. It's worth noting that if your price column is nullable then count(price) and count(*) may give different results; if that is the case you might want to get both counts as columns in your MV anyway.

Refresh view in HSQL

I would like to update a view in HSQL without writing the same statement again and again.
I have a table CONTACTS with ID, First_NAME and LAST_NAME. I also have a VIEW for this table, which I created with
CREATE VIEW IDGREATERTHREE AS SELECT * FROM CONTACTS WHERE ID > 3;
How can I update my VIEW after I added a new column to my table. I want to update my table without anything like this:
ALTER VIEW IDGREATERTHREE AS SELECT * FROM CONTACTS WHERE ID > 3;
I would like to find a way to refresh my invalid view in a similar way like in Oracle:
ALTER VIEW IDGREATERTHREE COMPILE;
I am also looking for a way to select just the invalid views. WithSELECT * FROM INFORMATION_SCHEMA.VIEWS I am not able to see any difference between an invalid and a non-invalid view.
A solution for this would be to write an ON DDL trigger.
In this ON DDL trigger , you check if your modifying your table.
If this is the case, then you use Dynamic SQL to recreate your view. This is doable with plsql (you tagged with oracle). There is ample documentation on creating triggers and dynamic SQL on the Internet.
HSQLDB cannot have invalid views. When you create a view, the SELECT * FROM CONTACTS is expanded to the actual column names. When you add a column to the table the view is recompiled with the original column names and the new column is not included.

How to auto-redefine view when underlying table changes (new column)?

We've got a view that's defined like this
CREATE VIEW aView as
SELECT * from aTable Where <bunch of conditions>;
The "value" of the view is in the where-condition, so it is okay to use a Select * in this case.
When a new column is added to the underlying table, we have to redefine the view with a
CREATE OR REPLACE FORCE VIEW aView as
SELECT * from aTable Where <bunch of conditions>;
as the Select * seems to get "translated" into all the columns present at the time the view is (re-)defined.
My question: How can we avoid this extra step?
(If the answer is dependent on the RDBMS, we're using Oracle.)
I know you specified Oracle, but the behavior is the same in SQL Server.
One way to update the view with the new column is to use:
exec sp_refreshview MyViewName
go
Of course, I also agree with the other comments about not using a SELECT * in a view definition.
This extra step is mandatory in Oracle: you will have to recompile your view manually.
As you have noticed, the "*" is lost once you create a view:
SQL> create table t (id number);
Table created
SQL> create view v as select * from t;
View created
SQL> select text from user_views where view_name = 'V';
TEXT
-------------------------------------------------------
select "ID" from t
You should not be using * in your views. Specify the columns explicitly.
That way you are only retrieving the data you need, and thus avoid potential issues down the road where someone adds a column to a table that you do not want that view to return (e.g., a large binary column that would adversely impact performance).
Yes, you need to recompile the view to add another column, but this is the correct process. That way you avoid other compilation issues, such as if the view reference two tables, and someone adds a duplicate column name in one of the tables. The compiler would then have issues determining which of the columns was being referred to if you did not prefix a reference to the column with a table alias, or it might complain if there are duplicate column names in the results.
The problem with automatically updating views to add columns comes when you extend your model, for example to
SELECT a.*, std_name_format(a.first_name, a.middle_names, a.last_name) long_name
or even
SELECT a.*, b.* from table_a a join table_b b....
If you have a view of just SELECT * FROM table, then you probably should be using a synonym or addressing the table directly.
If the view is hiding rows (SELECT * FROM table WHERE...), then you can look at the feature variously known as Fine Grained Access Control (FGAC), Row Level Security (RLS) or Virtual Private Database (VPD).
You might be able to do something with a DDL trigger but that would get complicated.