Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Manoj .

Asked: May 02, 2000 - 1:21 pm UTC

Last updated: February 15, 2010 - 10:12 am UTC

Version:

Viewed 10K+ times! This question is

You Asked

Dear tom,

We have a table having the following structure
sw_emp_no char(10) not null

Now this table is owned by user ADMIN. Now,we have created a view
on the table in a different schema .When we want to delete
records from this table from the other schema, we am prompted
with the following error by Oracle
The whole problem is as follows.

This is the view defining query:
create or replace view SWA_EMP_PROC as select a.* from ADMIN.SWA_EMP_PROC
a,mst_emp_master b where a.SW_EMP_NO = b.ms_emp_no

On deletion from the view we are prompted with the error:

ORA-01752: cannot delete from view without exactly one key-preserved table

A similar definition of the same view doesn't however give an
error on deletion from swa_emp_proc view.

CREATE OR REPLACE VIEW SWA_EMP_PROC AS SELECT * FROM ADMIN.SWA_EMP_PROC
WHERE SW_EMP_NO IN (SELECT MS_EMP_NO FROM MST_EMP_MASTER)








and Tom said...



Similar --- but very different. Consider this annotated example. The solution, if your model supports it, is to put a UNIQUE constraint on mst_emp_master(ms_emp_no).

ops$tkyte@8i> create table swa_emp_proc( sw_emp_no char(10) not null );
Table created.

ops$tkyte@8i> create table mst_emp_master( ms_emp_no char(10) not null );
Table created.

ops$tkyte@8i> insert into swa_emp_proc values ( 1 );
ops$tkyte@8i> insert into mst_emp_master values ( 1 );
ops$tkyte@8i> insert into mst_emp_master values ( 1 );
ops$tkyte@8i> commit;
Commit complete.



Here are your views:

ops$tkyte@8i> create or replace view SWA_EMP_PROC_JOIN as select a.* from SWA_EMP_PROC
2 a,mst_emp_master b where a.SW_EMP_NO = b.ms_emp_no
3 /
View created.

ops$tkyte@8i> CREATE OR REPLACE VIEW SWA_EMP_PROC_IN AS SELECT * FROM SWA_EMP_PROC
2 WHERE SW_EMP_NO IN (SELECT MS_EMP_NO FROM MST_EMP_MASTER)
3 /
View created.



ops$tkyte@8i> REM this shows they can be different in the data they return:

ops$tkyte@8i> select * from SWA_EMP_PROC_JOIN;

SW_EMP_NO
----------
1
1

ops$tkyte@8i> select * from SWA_EMP_PROC_IN;

SW_EMP_NO
----------
1

ops$tkyte@8i> REM this shows you cannot delete from the JOIN view since the
ops$tkyte@8i> REM rows in swa_emp_proc are 'multiplied' -- you get a row back
ops$tkyte@8i> REM for each row in mst_emp_master
ops$tkyte@8i> REM It is ambigous how the delete should proceed on this.
ops$tkyte@8i> REM consider "delete form swa_emp_proc_join WHERE ROWNUM = 1"
ops$tkyte@8i> REM that would in effect delete 2 rows -- but it cannot -- its not defined
ops$tkyte@8i> REM so we don't permit it to happen:


ops$tkyte@8i> delete from SWA_EMP_PROC_JOIN;
delete from SWA_EMP_PROC_JOIN
*
ERROR at line 1:
ORA-01752: cannot delete from view without exactly one key-preserved table


but we can delete from the IN since the rows in A are not 'multiplied'

ops$tkyte@8i> delete from SWA_EMP_PROC_IN;
1 row deleted.

ops$tkyte@8i> rollback;
Rollback complete.

Lets get rid of the one row that messes this up:

ops$tkyte@8i> delete from mst_emp_master where rownum = 1;
1 row deleted.

and add a unique constraint:

ops$tkyte@8i> alter table mst_emp_master add constraint ms_emp_no_unique unique(ms_emp_no);
Table altered.

ops$tkyte@8i> REM Now, we have the constraint that makes it so that the rows in
ops$tkyte@8i> REM swa_emp_proc are *not* multiplied by rows in mst_emp_master.
ops$tkyte@8i> REM now we have a key preserved join and can delete from it
ops$tkyte@8i>
ops$tkyte@8i> delete from SWA_EMP_PROC_JOIN;

1 row deleted.

Hope this clears up the confusion.





Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

ORA-01752: cannot delete from view without exactly one key-preserved table

Parthiban Nagarajan, January 28, 2010 - 7:09 am UTC

Hi Tom

create table tname_a (x int primary key);
create table tname_b (x int primary key references tname_a);
delete (
select a.x, b.x from tname_a a, tname_b b
);

delete (
*
ERROR at line 1:
ORA-01752: cannot delete from view without exactly one key-preserved table

Why we are not able to delete from two tables at a time (in single SQL) ?
Oracle can decide the relationship with the data dictionary and then can delete TNAME_B first and then delete TNAME_A ... (If we can interpret this from the above script, ORACLE can do a lot regarding this, I hope ...)
If so, why it is raising the error ORA-01752 ?
Tom Kyte
January 29, 2010 - 3:41 pm UTC

... Why we are not able to delete from two tables at a time (in single SQL) ? ...

because it is against the rules, that is all. If we cannot determine which SINGLE TABLE is the target of the delete - we cannot do it.

If you want to, you could write an instead of delete trigger to delete from both.



Re: ORA-01752

Parthiban Nagarajan, February 01, 2010 - 9:53 pm UTC

Hi Tom

Thanks for your reply. But my doubt seems not cleared yet.
<quote> ... we cannot determine which SINGLE TABLE is the target of the delete ... </quote>
Why you want to target only a SINGLE TABLE ?
If I want it to be a single table, I would have written like ...
delete from tname_a;
But, I need to delete from more than one table in a single SQL. Thats why I have written ...
delete (a_query_with_join);
So, can you please tell me why Oracle wants to delete only from a Single Table ?

Tom Kyte
February 02, 2010 - 12:15 pm UTC

... Why you want to target only a SINGLE TABLE ? ..

because the rule of DELETE is... you delete from a single table, period, that is it, that is the rule, that is the standard, that is the definition of DELETE.


It is not Oracle, is is SQL.



... But, I need to delete from more than one table in a single SQL....

no, you don't need to delete from more than one table, you can and will use a delete per table.

Re: ORA-01752

Parthiban Nagarajan, February 02, 2010 - 8:37 pm UTC

Hi Tom

Thanks for your explanation.
I am not happy with this rule of SQL. :(
Let it be the rule of SQL but not the rule of Oracle itself. ;)
It would be very useful and interesting if Oracle provides the multi-table delete in single SQL in coming versions.

Once again, Thanks.
Tom Kyte
February 03, 2010 - 9:55 am UTC

.. It would be very useful and interesting if Oracle provides the multi-table
delete in single SQL in coming versions.
...


I disagree.

It would ONLY make sense in a 1:1 relationship. Never in a one to many.

And if you ask me, a 1:1 relationship is something that is OK in a logical model, but you do not do that in a physical design. There would be one table.

Hence, if you find yourself constantly in need of this, in my opinion, you've done it wrong.


unpredictability of the deleted table in a DELETE JOIN

Vincent Malgrat, February 08, 2010 - 5:03 am UTC

Greetings Tom,

I agree with you that only one table will be deleted in a DELETE JOIN, however I have found that when several tables are key-preserved there is no raised exception (in contradiction with the error message "exactly one key-preserved table"). In the case when there are two key-preserved tables, IMO Oracle should raise an exception because there is an ambiguity as to which table will be deleted. Instead, Oracle seems to choose the first table in the FROM list (?!):

SQL> select * from v$version;
 
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bi
PL/SQL Release 10.2.0.3.0 - Production
CORE 10.2.0.3.0 Production
TNS for IBM/AIX RISC System/6000: Version 10.2.0.3.0 - Productio
NLSRTL Version 10.2.0.3.0 - Production

SQL> CREATE TABLE tname_a (x INT PRIMARY KEY);
 
Table created
SQL> CREATE TABLE tname_b (x INT PRIMARY KEY);
 
Table created
SQL> INSERT INTO tname_a VALUES (1);
 
1 row inserted
SQL> INSERT INTO tname_a VALUES (2);
 
1 row inserted
SQL> INSERT INTO tname_b VALUES (1);
 
1 row inserted
SQL> DELETE (SELECT a.x, b.x FROM tname_a a, tname_b b WHERE a.x = b.x);
 
1 row deleted
SQL> SELECT * FROM tname_a;
 
                                      X
---------------------------------------
                                      2
SQL> SELECT * FROM tname_b;
 
                                      X
---------------------------------------
                                      1
 
SQL> -- ** We see that Oracle deleted one row in tname_a,
SQL> -- let's try to reverse the order of the DELETE **:
SQL> INSERT INTO tname_a VALUES (1);
 
1 row inserted
 
SQL> DELETE (SELECT a.x, b.x FROM tname_b b, tname_a a WHERE a.x = b.x);
 
1 row deleted
 
SQL> 
SQL> SELECT * FROM tname_a;
 
                                      X
---------------------------------------
                                      1
                                      2
SQL> SELECT * FROM tname_b;
 
                                      X
---------------------------------------
SQL> -- ** Here Oracle deleted one row in tname_b **

Tom Kyte
February 15, 2010 - 10:12 am UTC

that is the behavior of DELETE since version 9i, I filed a documentation bug against that a while ago and it is updated in the current doc set:

http://docs.oracle.com/docs/cd/E11882_01/server.112/e10595/views001.htm#ADMIN11786

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library