Delete with Left Join in Oracle 10g

前端 未结 5 1774
臣服心动
臣服心动 2020-12-05 19:33

I have the following code that works fine in MS SQL Server:

delete grp
from grp
left join my_data
on grp.id1 = my_data.id1
and grp.id2 = my_data.id2
and grp.         


        
相关标签:
5条回答
  • 2020-12-05 19:49

    Either Vincent's answer https://stackoverflow.com/a/3675205 does not work at all, or it does not work in Oracle 12c. That answer should be improved by specifying the lowest or highest version of Oracle where this works. The proof:

    SELECT * FROM v$version where banner like 'Oracle%';
    /*
    Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production
    */
    create table a (id int);
    create table b (id int);
    insert into a select 1 from dual union select 2 from dual;
    insert into b select 1 from dual union select 2 from dual union select 3 from dual;
    select * from a right join b on b.id = a.id;
    /*
    1   1
    2   2
    null    3
    */
    delete from (
      select b.*
      from b
      inner join a on a.id = b.id
    )    
    /*
    Error at Command Line : 7 Column : 13
    Error report -
    SQL Error: ORA-01752: cannot delete from view without exactly one key-preserved table
    01752. 00000 -  "cannot delete from view without exactly one key-preserved table"
    *Cause:    The deleted table had
               - no key-preserved tables,
               - more than one key-preserved table, or
               - the key-preserved table was an unmerged view.
    *Action:   Redefine the view or delete it from the underlying base tables.
    */
    
    delete from b
    where rowid in (
      select b.rowid
      from b
      inner join a on a.id = b.id
    )
    /*
    2 rows deleted.
    */
    select * from a right join b on b.id = a.id
    /*
    null  3
    */
    
    drop table a;
    drop table b;
    

    Bottom line is, use WHERE ROWID IN () at least in 12c.

    0 讨论(0)
  • 2020-12-05 19:56

    Tables and data:

    SQL> create table grp (id1 number null, id2 number null, id3 number null, id4 number null);    
    Table created.
    
    SQL> create table my_data (id1 number null, id2 number null, id3 number null, id4 number null);
    
    Table created.
    
    SQL> insert into grp values (1, 2, 3, 4);
    
    1 row created.
    
    SQL> insert into grp values (10, 20, 30, 40);
    
    1 row created.
    
    SQL> insert into grp values (1, 2, 30, 40);
    
    1 row created.
    
    SQL> insert into my_data values (1, 2, 3, 4);
    
    1 row created.
    
    SQL> commit;
    
    Commit complete.
    

    Using in. Note Do not use if the IDs in the subquery can be null. Not in of null never returns true.

    SQL> delete grp where (id1, id2, id3, id4) not in (select id1, id2, id3, id4 from my_data);
    
    2 rows deleted.
    
    SQL> select * from grp;
    
           ID1        ID2        ID3        ID4
    ---------- ---------- ---------- ----------
             1          2          3          4
    

    Using exists

    SQL> rollback;
    
    Rollback complete.
    
    SQL> delete grp where not exists (select * from my_data where grp.id1 = my_data.id1 and grp.id2 = my_data.id2 and grp.id3 = my_data.id3 and grp.id4 = my_data.id4);
    
    2 rows deleted.
    
    SQL> select * from grp;
    
           ID1        ID2        ID3        ID4
    ---------- ---------- ---------- ----------
             1          2          3          4
    
    SQL> 
    
    0 讨论(0)
  • 2020-12-05 20:01

    I can't add a comment because it need 50 reps,so I add a answer here.

    I tested Vincent's delete from query, that syntax can't let you delete what you want,at least it's not a common use for all the delete join cases.

    At first I create a table using oracle default user scott:

    create table emp1 as select * from emp where sal<2000;
    

    I want to delete the records from emp where empno in emp1(just a simple test),so I used this delete from query:

    delete from (select a.* from emp a join emp1 b on a.empno=b.empno);
    

    No matter what the table or join order is,left join or inner join,no matter what where clause I use,the sql will delete the corresponding records in emp1.

    So I think this delete from query can not let you delete from a specified table. Loop a cursor will be a better way for these cases.

    0 讨论(0)
  • 2020-12-05 20:03

    If you want to ensure there is no ambiguity in what's being deleted, you could change Vincent's solution to:

    delete from grp where rowid in
        (
        select
             grp.rowid
        from
             grp left outer join my_data on
                grp.id1 = my_data.id1
            and grp.id2 = my_data.id2
            and grp.id3 = my_data.id3
            and grp.id4 = my_data.id4
        where
            my_data.id1 is NULL
        )
    
    0 讨论(0)
  • 2020-12-05 20:10

    Shannon's solution is the way to go: use the operator NOT IN (or NOT EXISTS).

    You can however delete or update a join in Oracle, but the synthax is not the same as MS SQL Server:

    SQL> DELETE FROM (SELECT grp.*
      2                  FROM grp
      3                  LEFT JOIN my_data ON grp.id1 = my_data.id1
      4                                   AND grp.id2 = my_data.id2
      5                                   AND grp.id3 = my_data.id3
      6                                   AND grp.id4 = my_data.id4
      7                 WHERE my_data.id1 IS NULL);
    
    2 rows deleted
    

    Additionally, Oracle will only let you update a join if there is no ambiguity as to which base row will be accessed by the statement. In particular, Oracle won't risk an update or a delete (the statement will fail) if there is a possibility that a row may appear twice in the join. In this case, the delete will only work if there is a UNIQUE constraint on my_data(id1, id2, id3, id4).

    0 讨论(0)
提交回复
热议问题