When I updated a view which was created using a base table, the updation affected the base table as well. How is that possible? If view is considered as just a 'window' through which we can see a set of data of the base table then how can the base table change when I try to change the data inside a view.
You can make changes to the state of underlying table using the view as long as the you are targeting the change in single table.
View is a security layer on top of table object and allows most of the DML operation as long as you do not violet the base rule.
Example:
CREATE TABLE T1
(ID INT IDENTITY(1,1), [Value] NVARCHAR(50))
CREATE TABLE T2
(ID INT IDENTITY(1,1), [Value] NVARCHAR(50))
--Dummy Insert
INSERT INTO T1 VALUES ('TestT1')
INSERT INTO T2 VALUES ('TestT2')
--Create View
CREATE VIEW V1
AS
SELECT T1.ID AS T1ID, T2.ID AS T2ID, T1.Value AS T1Value, T2.Value AS T2Value FROM T1 INNER JOIN T2
ON T2.ID = T1.ID
--Check the result
SELECT * FROM V1
--Insert is possible via view as long as it affects only one table
INSERT INTO V1 (T1Value) VALUES
('TestT1_T1')
INSERT INTO V1 (T2Value) VALUES
('TestT2_T2')
--Change is possible only if target is only one table
UPDATE V1
SET T1Value = 'Changed'--**
WHERE T2ID = 1
--This is not allowed
INSERT INTO V1 (T1Value, T2Value) VALUES
('TestT1_T1','TestT2_T2')
--Msg 4405, Level 16, State 1, Line 1
--View or function 'V1' is not updatable because the modification affects multiple base tables.
--Check T1 and T2 with each statement to see how it gets affected
--
In some databases it's possible to update the source table(s) for a view if there is a one-to-one relationship between the rows in the view and the rows in the underlying table, that is, you cant have derived columns, aggregate functions or a distinct clause in your view for example.
In Oracle, even if a view is not inherently updatable, updates may be allowed if an INSTEAD OF DML trigger is defined.
If you use mysql, you can read a detailed description about this feature Updatable and insertable views.
" If view is considered as just a 'window' through which we can see a set of data of the base table "
- Where did you get this definition?
What oracle says about views:
A view is a logical representation of another table or combination of
tables. A view derives its data from the tables on which it is based.
These tables are called base tables. Base tables might in turn be
actual tables or might be views themselves. All operations performed
on a view actually affect the base table of the view. You can use
views in almost the same way as tables. You can query, update, insert
into, and delete from views, just as you can standard tables.
Such a view into which you can update or insert are fondly named as "Updatable and Insertable Views". Oracle documentation about them is here.
Also, this is how the purpose of an "insert" statement is defined by Oracle:
Use the INSERT statement to add rows to a table, the base table of a
view, a partition of a partitioned table or a subpartition of a
composite-partitioned table, or an object table or the base table of
an object view.
Yes we can achieve the DML Operation in Views like belows:
Create or replace view emp_dept_join as Select d.department_id,
d.department_name, e.first_name, e.last_name from employees
e, departments d where e.department_id = d.department_id;
SQL>CREATE OR REPLACE TRIGGER insert_emp_dept
INSTEAD OF INSERT ON emp_dept_join DECLARE v_department_id departments.department_id%TYPE;
BEGIN
BEGIN
SELECT department_id INTO v_department_id
FROM departments
WHERE department_id = :new.department_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO departments (department_id, department_name)
VALUES (dept_sequence.nextval, :new.department_name)
RETURNING ID INTO v_department_id;
END;
INSERT INTO employees (employee_id, first_name, last_name, department_id)
VALUES(emp_sequence.nextval, :new.first_name, :new.last_name, v_department_id);
END insert_emp_dept;
/
if the viwe is defined through a simple query involving single base relation and either containing primary key or candidate key, so there will be change in base relation if changing the view. ( however there is restriction)
And updates are not allowed through view if there is multiple base relations or grouping operations.
Related
For performance needs I want to create a materialized view on commit refresh option using the following script:
CREATE TABLE DEVDV
(DEVDV_ID INTEGER PRIMARY kEY,
DEVDV_SRC_DVISE_ID INTEGER,
DEVDV_CIB_DVISE_ID INTEGER);
CREATE TABLE CONDV
(CONDV_ID INtEgEr PRiMARY KEY,
CONDV_DEVDV_iD INTEGER,
CONDV_TX NUMbeR,
CONDV_DATE_DEB datE,
CONDV_DATE_FIN DATE);
CREATE MATERIALIZED VIEW LOG ON DEVDV WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON CONDV WITH ROWID;
CREATE MATERIALIZED VIEW MV_DEVDV_TYP_2
REFRESH FAST
ON COMMIT
AS
SELECT DEVDV.ROWID CROWID,
CONDV.ROWID DROWID,
DEVDV_ID,
1 AS MARKER,
DEVDV_SRC_DVISE_ID,
DEVDV_CIB_DVISE_ID,
CONDV_TX,
CONDV_DATE_DEB,
CONDV_DATE_FIN
FROM
DEVDV INNER JOIN CONDV ON DEVDV_ID = CONDV_DEVDV_ID
UNION ALL
SELECT DEVDV.ROWID CROWID,
CONDV.ROWID DROW_ID,
DEVDV_ID,
2 AS MARKER,
DEVDV_CIB_DVISE_ID,
DEVDV_SRC_DVISE_ID,
1/CONDV_TX,
CONDV_DATE_DEB,
CONDV_DATE_FIN
FROM
DEVDV INNER JOIN CONDV ON DEVDV_ID = CONDV_DEVDV_ID;
Oracle says that it's a complex query and it doesn't meet the fast refresh requirements,
Could you please tell me which rule I've broken?
I don't know why, but - for materialized views in oracle - you have to use old syntax for joins. So put all tables in FROM separating them with commas, and join condition put in where clause (use "(+)" for outer joins).
That works for me:
CREATE MATERIALIZED VIEW MV_DEVDV_TYP_2
REFRESH FAST
ON COMMIT
AS
SELECT DEVDV.ROWID CROWID,
CONDV.ROWID DROWID,
DEVDV_ID,
1 AS MARKER,
DEVDV_SRC_DVISE_ID,
DEVDV_CIB_DVISE_ID,
CONDV_TX,
CONDV_DATE_DEB,
CONDV_DATE_FIN
FROM
DEVDV, CONDV
WHERE DEVDV_ID = CONDV_DEVDV_ID
UNION ALL
SELECT DEVDV.ROWID CROWID,
CONDV.ROWID DROW_ID,
DEVDV_ID,
2 AS MARKER,
DEVDV_CIB_DVISE_ID,
DEVDV_SRC_DVISE_ID,
1/CONDV_TX,
CONDV_DATE_DEB,
CONDV_DATE_FIN
FROM
DEVDV, CONDV
WHERE DEVDV_ID = CONDV_DEVDV_ID;
Materialized view MV_DEVDV_TYP_2 created.
I am using Toad for oracle 12c. I need to copy a table and data (40M) from one shcema to another (prod to test). However there is an unique key(not the PK for this table) called record_Id col which has something data like this 3.000*******19E15. About 2M rows has same numbers(I believe its because very large number) which are unique in prod. When I try to copy it violets the unique key of that col. I am using toad "export data to another schema" function to copy the data.
when I execute query in prod
select count(*) from table_name
OR
select count(distinct(record_id) from table_name
Both query gives the exact same numbers of data.
I don't have DBA permission. How do I copy all data without violating unique key of the table.
Thanks in advance!
You can use UPSERT for decisional INSERT or UPDATE or you may write small procedure for this.
you may consider to use NOT EXISTS, but your data is big and it might not be resource efficient.
insert into prod_tab
select * from other_tab t1 where NOT exists (
select 1 from prod_tab t2 where t1.id = t2.id
);
In Oracle you can use a MERGE query for that.
The following query proceeds as follows for each data row :
if the source record_id does not yet exist in the target table, a new record is inserted
else, the existing record is updated with source values
For the sake of the example, I assumed that there are two other columns in the table : column1 and column2.
MERGE INTO target_table t1
USING (SELECT * from source_table t2)
ON (t1.record_id = t2.record_id)
WHEN MATCHED THEN UPDATE SET
t1.column1 = t2.column1,
t1.column2 = t2.column2
WHEN NOT MATCHED THEN INSERT
(record_id, column1, column2) VALUES (t2.record_id, t2.column1, t2.column2)
I have one table and view where one column is common which is the primary key of the table. Now if I want to join the table and view only with specific columns, should I create view or table in that case? Also I want to import the joined result to Tableau.
Well If you want just join table and view in single query you may write it, or you may create view for it, if you want. For example:
create table tmp_table_a (id, first_col, second_col, third_col) as
select level, lpad('a',level,'b'), lpad('c',level,'d'), lpad('e',level,'f')
from dual connect by level < 101;
create view v_tmp_a as
select id, substr(first_col,1,10) as first_sub_col from tmp_table_a;
simple query:
select second_col, third_col, first_sub_col
from tmp_table_a t1, v_tmp_a v1
where t1.id = v1.id;
or create view:
create view v_join_a as
select second_col, third_col, first_sub_col
from tmp_table_a t1, v_tmp_a v1
where t1.id = v1.id;
select * from v_join_a;
I have a table that contains rows belonging to various users. I want to CREATE A VIEW in a Stored Procedure to work on only the records that belong to me, i.e. CREATE VIEW V1 AS SELECT * FROM T1 WHERE USER_ID = Z.
The user_id z is passed to the Stored Procedure as a Parameter
As I have to carry out many statements on my rows, I do not want to specify "WHERE USER_ID = Z" in all the statements.
However, I find that the CREATE VIEW statement does not accept a parameter.
Is there any solution to create a sub-set of the table containing only the rows that belong to me?
I am testing this approach.
CREATE TEMPORARY TABLE T2 LIKE T1;
INSERT INTO T2 SELECT * FRON T1 WHERE USER_ID = Z;
I will carry out all the manipulation using T2
At the end of the stored procedure, I will update T1 FROM T2 as follows.
DELETE T1 WHERE USER_ID = Z;
INSERT INTO T1 SELECT * FROM T2;
Any better solution?
I had difficulty with the use of the Temporary Table. It does not allow the table to be re-opened as another table under the following format,
SELECT TEMP AS T1 JOIN TEMP AS T2 .....
So I am stuck with the problem.
Hope someone can offer a solution. I cannot understand why VIEW does not allow a parameter to be passed to it, as it seem to be a good approach to work on a sub-set of the table.
I have a question regarding a unified insert query against tables with different data
structures (Oracle). Let me elaborate with an example:
tb_customers (
id NUMBER(3), name VARCHAR2(40), archive_id NUMBER(3)
)
tb_suppliers (
id NUMBER(3), name VARCHAR2(40), contact VARCHAR2(40), xxx, xxx,
archive_id NUMBER(3)
)
The only column that is present in all tables is [archive_id]. The plan is to create a new archive of the dataset by copying (duplicating) all records to a different database partition and incrementing the archive_id for those records accordingly. [archive_id] is always part of the primary key.
My problem is with select statements to do the actual duplication of the data. Because the columns are variable, I am struggling to come up with a unified select statement that will copy the data and update the archive_id.
One solution (that works), is to iterate over all the tables in a stored procedure and do a:
CREATE TABLE temp as (SELECT * from ORIGINAL_TABLE);
UPDATE temp SET archive_id=something;
INSERT INTO ORIGINAL_TABLE (select * from temp);
DROP TABLE temp;
I do not like this solution very much as the DDL commands muck up all restore points.
Does anyone else have any solution?
How about creating a global temporary table for each base table?
create global temporary table tb_customers$ as select * from tb_customers;
create global temporary table tb_suppliers$ as select * from tb_suppliers;
You don't need to create and drop these each time, just leave them as-is.
You're archive process is then a single transaction...
insert into tb_customers$ as select * from tb_customers;
update tb_customers$ set archive_id = :v_new_archive_id;
insert into tb_customers select * from tb_customers$;
insert into tb_suppliers$ as select * from tb_suppliers;
update tb_suppliers$ set archive_id = :v_new_archive_id;
insert into tb_suppliers select * from tb_suppliers$;
commit; -- this will clear the global temporary tables
Hope this helps.
I would suggest not having a single sql statement for all tables and just use and insert.
insert into tb_customers_2
select id, name, 'new_archive_id' from tb_customers;
insert into tb_suppliers_2
select id, name, contact, xxx, xxx, 'new_archive_id' from tb_suppliers;
Or if you really need a single sql statement for all of them at least precreate all the temp tables (as temp tables) and leave them in place for next time. Then just use dynamic sql to refer to the temp table.
insert into ORIGINAL_TABLE_TEMP (SELECT * from ORIGINAL_TABLE);
UPDATE ORIGINAL_TABLE_TEMP SET archive_id=something;
INSERT INTO NEW_TABLE (select * from ORIGINAL_TABLE_TEMP);