Materialized view logs questions - oracle

I have a materialized view based on two or more other materialized views. I want to schedule fast refresh for the materialized view but the issue is that it does not have any logs, so I must create the logs first. I am new to materialized views so I am not sure how to go about creating the logs for the two underlying materialized views. Do I create a log for each underlying table that is utilized by those views?

Assuming you want everything to be fast refreshable, you need MV logs on the:
base tables
MVs underlying the "final" MV
You create MV logs on an MV in the same way as regular tables:
create table t1 (
c1 int primary key, c2 int
);
create table t2 (
c1 int, c2 int, primary key ( c1, c2 )
);
create materialized view log on t1
with rowid, primary key ( c2 )
including new values;
create materialized view log on t2
with rowid, primary key
including new values;
create materialized view mv1
refresh fast on commit as
select * from t1;
create materialized view mv2
refresh fast on commit as
select * from t2;
create materialized view log on mv1
with rowid ( c1, c2 )
including new values;
create materialized view log on mv2
with rowid ( c1, c2 )
including new values;
create materialized view mv3
refresh fast on commit as
select mv1.c1, count (*)
from mv1
join mv2
on mv1.c1 = mv2.c1
group by mv1.c1;
insert into t1 values ( 1, 1 );
insert into t1 values ( 2, 2 );
insert into t2 values ( 1, 1 );
insert into t2 values ( 1, 2 );
insert into t2 values ( 2, 2 );
commit;
select * from mv3;
C1 COUNT(*)
---------- ----------
1 2
2 1

Related

query to get all tables in a materialized view

Good afternoon friends,
a query is there any way (select * from) to visualize which tables form a materialized view?
ex:
CREATE MATERIALIZED VIEW table_vm
REFRESH COMPLETE ON COMMIT
as
SELECT * FROM table1;
UNION ALL
SELECT * FROM table2;
I would like to output something like this:
view name | table name
table_m | Table 1
table_m | table 2
tabla_m | table 3
....
....
Thank you so much,
I would appreciate any information.
You can use the view DBA_DEPENDENCIES to view any dependencies for an object compiled into the database. Querying that view with the name of your materialized view should list all of the tables as well as any other dependencies the materialized view relies on. REFERENCED_OWNER and REFERENCED_NAME will hold the values of the tables being used by the materialized view.
SELECT *
FROM dba_dependencies
WHERE owner = 'OWNER_OF_MV' AND name = 'TABLE_MV';
One option would be
SQL> create table t1 ( c1 number, c2 varchar2(1) ) ;
Table created.
SQL> create table t2 ( c1 number , c3 varchar2(1) ) ;
Table created.
SQL> create table t3 ( c1 number , c3 varchar2(1) ) ;
Table created.
SQL> create materialized view mv1 refresh complete on demand as
2 select a.c1 , b.c3 as c2, c.c3
3 from t1 a inner join t2 b on a.c1 = b.c1
4 left join t3 c on a.c1 = c.c1 ;
Materialized view created.
SQL> select name as mv, listagg(referenced_name || ' - ' || referenced_type , '|' )
within group ( order by referenced_name ) as list_dep
from dba_dependencies where name='MV1' and name != referenced_name
group by name
MV LIST_DEP
------------------------------ --------------------------------------------------
MV1 T1 - TABLE|T2 - TABLE|T3 - TABLE

Referencing results of one query in another in Apache Nifi

We are trying to migrate data from Oracle to Elasticsearch using Apache Nifi.
We are trying to establish a one to many relationships(represented as multiple tables in Oracle) in a single elastic index.
What we are trying to achieve can be summarized as below.
select * from table1. (The primary key of table1 is key1)
For each fetched record, We want to extract data from another table using the key from table 1. Something like
select * from table2 where foreign_key = key1.
We checked the ExecuteSQLRecord processor which has select query and post query but are unable to figure out how to reference key from table1 in the query to table2
Please let us know if there are any other processors specifically designed for this use case.
There are several ways to achieve this
Creating Views in Oracle
You can create views in Oracle to build the queries that make the relationship primarykey-foreingkey. Thereby you can select directly from the view instead.
A small example
SQL> create table testpk ( c1 number , c2 number );
Table created.
SQL> alter table testpk add primary key ( c1 ) ;
Table altered.
SQL> create table testfk ( c3 number , c4 number );
Table created.
SQL> alter table testfk add constraint fk_to_testpk FOREIGN KEY (c3) references testpk(c1) ;
Table altered.
SQL> insert into testpk values ( 1 , 1);
1 row created.
SQL> insert into testfk values ( 1 , 2 );
1 row created.
SQL> insert into testpk values ( 2 , 2 );
1 row created.
SQL> insert into testfk values ( 2 , 2 );
1 row created.
SQL> commit;
Commit complete.
SQL> create or replace force view my_test_view as select a.c1 , a.c2 , b.c3 , b.c4
2 from testpk a join testfk b on ( a.c1 = b.c3 ) ;
View created.
SQL> select * from my_test_view ;
C1 C2 C3 C4
---------- ---------- ---------- ----------
1 1 1 2
2 2 2 2
SQL>
Use queries directly
In your case, you need to run the query to make the relationship against the parent table, therefore you need a join:
select * from table2 inner join table1 where table2_foreingkey = table1_primarykey.
You want all records from table2 where the relationship parent key table 1 - child key table 2 matches, something you can do it with a normal inner join
If you ask me, I would create views to make the process more transparent in elastic search.

DML on table with the same name as Mview

For some reason, I should keep the Mview which has the same name as a base table.
Can you let me know how to issue DML on a base table in this case?
As you can see in the below example, I wanted to issue DML for the base table, however, Mview is considered at the first.
DROP TABLE SRC_TABLE PURGE;
DROP TABLE TGT_TABLE PURGE;
DROP MATERIALIZED VIEW TGT_TABLE;
DROP MATERIALIZED VIEW LOG ON SRC_TABLE ;
CREATE TABLE SRC_TABLE(X NUMBER(8) PRIMARY KEY);
CREATE TABLE TGT_TABLE(X NUMBER(8) PRIMARY KEY);
INSERT INTO SRC_TABLE VALUES(55);
COMMIT;
CREATE MATERIALIZED VIEW LOG ON SRC_TABLE WITH PRIMARY KEY, ROWID;
CREATE MATERIALIZED VIEW TGT_TABLE
ON PREBUILT TABLE WITH REDUCED PRECISION
USING INDEX
REFRESH FAST ON DEMAND
WITH PRIMARY KEY USING DEFAULT LOCAL ROLLBACK SEGMENT
USING ENFORCED CONSTRAINTS DISABLE ON QUERY COMPUTATION DISABLE QUERY REWRITE
AS
SELECT * FROM SRC_TABLE
/
INSERT INTO SRC_TABLE VALUES (10);
INSERT INTO SRC_TABLE VALUES (20);
COMMIT;
EXEC DBMS_MVIEW.REFRESH('TGT_TABLE');
SELECT * FROM SRC_TABLE;
SELECT * FROM TGT_TABLE;
SQL> DELETE FROM TGT_TABLE;
DELETE FROM TGT_TABLE
*
ERROR at line 1:
ORA-01732: data manipulation operation not legal on this view
TGT_TABLE is a physical table which is used by the materialized view as a "storage"
SRC_TABLE is a table which is used as the "source" of data for that materialized view
you
can't modify the materialized view or the underlying table which is used as its storage
can modify table which is used as the source, and that would be the SRC_TABLE, not TGT_TABLE
It is kind of confusing because it looks like you have two objects having the same name, which is impossible. For example:
SQL> select object_name, object_type from user_objects where object_name = 'DEPT';
OBJECT_NAME OBJECT_TYPE
--------------- -------------------
DEPT TABLE
SQL> create materialized view dept as select * From dept;
create materialized view dept as select * From dept
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
SQL>
However, you chose to re-use existing table (TGT_TABLE; it is the ON PREBUILT TABLE clause) so it looks as if there were two objects with the same name. That's how materialized view is designed - has "query" (a "view" which is used to refresh data), and "physical storage" (a "table") which actually contains data.
If you didn't use table that already exists and created a materialized view on some table, you'd still see two objects with the same name. For example:
SQL> select object_name, object_type from user_objects where object_name = 'TEST';
no rows selected
SQL> create materialized view test as select * from dept;
Materialized view created.
SQL> select object_name, object_type from user_objects where object_name = 'TEST';
OBJECT_NAME OBJECT_TYPE
--------------- -------------------
TEST TABLE
TEST MATERIALIZED VIEW
See? Something what is impossible to achieve otherwise.
What you did was trying to modify the storage table, and it didn't work:
SQL> update test set loc = 'Zagreb' where deptno = 10;
update test set loc = 'Zagreb' where deptno = 10
*
ERROR at line 1:
ORA-01732: data manipulation operation not legal on this view
But, you can / should modify the table materialized view is created against:
SQL> update dept set loc = 'Zagreb' where deptno = 10;
1 row updated.
SQL>
Anyway, modifying the storage table doesn't make much sense as those changes would be overwritten at the next materialized view refresh.
So, in your case, you should update/delete SRC_TABLE, not TRG_TABLE.

oracle: primary key column in a materialized view

The task is to create a materialized view with the primary key on some unique column, so the materialized view could be replicated to another DB. The problem is that there are no unique columns because I had to join a very large number of tables, so I can not use the IDs from the joined tables, as they are not unique anymore in my materialized view.
First of all I have to create a view, because there are subqueries in the select:
CREATE VIEW V_CONTRACTS
AS
SELECT
ID.NEXTVAL,
C.*
FROM (SELECT <lots of columns>
FROM CONTRACT
<lots of joins>
WHERE <some filters>) C
;
But the error is thrown:
ORA-02287: sequence number not allowed here
Then I will create the materialized view as:
CREATE MATERIALIZED VIEW CONTRACTS
AS
SELECT * FROM V_CONTRACTS;
You could use the ROW_NUMBER analytic function to give you a number column.
CREATE VIEW V_TEST
AS
SELECT
ROW_NUMBER() OVER ( ORDER BY col1 , col2 ) as idx,
C.*
FROM ( SELECT 'A' as col1, 'B' as col2 FROM DUAL
UNION
SELECT 'C' as col1, 'D' as col2 FROM DUAL
UNION
SELECT 'E' as col1, 'F' as col2 FROM DUAL
) C ;
CREATE MATERIALIZED VIEW mv_test
AS
SELECT * FROM V_TEST ;
SELECT * FROM mv_test ;

Delete data returned from subquery in oracle

I have two tables. if the data in table1 is more than a predefined limit (say 2), i need to copy the remaining contents of table1 to table2 and delete those same contents from table1.
I used the below query to insert the excess data from table1 to table2.
insert into table2
SELECT * FROM table1 WHERE ROWNUM < ((select count(*) from table1)-2);
Now i need the delete query to delete the above contents from table1.
Thanks in advance.
A straightforward approach would be an interim storage in a temporary table. Its content can be used to determine the data to be deleted from table1 as well as the source to feed table 2.
Assume (slightly abusing notation) to be the PK column (or that of any candidate key) of table1 - usually there'll be some key that comprises only 1 column.
create global temporary table t_interim as
( SELECT <pk> pkc FROM table1 WHERE ROWNUM < ((select count(*) from table1)-2 )
;
insert into table2
select * from table1 where <pk> IN (
select pkc from t_interim
);
delete from table1 where <pk> IN (
select pkc from t_interim
);
Alternative
If any key of table1 spans more than 1 column, use an EXISTS clause instead as follows ( denoting the i-th component of a candidate key in table1):
create global temporary table t_interim as
( SELECT <ck_1> ck1, <ck_2> ck2, ..., <ck_n> ckn FROM table1 WHERE ROWNUM < ((select count(*) from table1)-2 )
;
insert into table2
select * from table1 t
where exists (
select 1
from t_interim t_i
where t.ck_1 = t_i.ck1
and t.ck_2 = t_i.ck2
...
and t.ck_n = t_i.ckn
)
;
delete from table1 t where
where exists (
select 1
from t_interim t_i
where t.ck_1 = t_i.ck1
and t.ck_2 = t_i.ck2
...
and t.ck_n = t_i.ckn
)
;
(Technically you could try to adjust the first scheme by synthesizing a key from the components of any CK, eg. by concatenating. You run the risk of introducing ambiguities ( (a bc, ab c) -> (abc, abc) ) or run into implementation limits ( max. varchar length ) using the first method)
Note
In case the table doesn't have a PK, you can apply the technique using any candidate key of table1. There will always be one, in the extreme case it's the set of all columns.
This situation may be the right time to improve the db design and add a (synthetic) pk column to table1 ( and any other tables in the system that lack it).

Resources