在Oracle 10g中使用左联接删除

问题描述 投票:15回答:4

我有以下代码可在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.id3 = my_data.id3
and grp.id4 = my_data.id4
where my_data.id1 is NULL

[基本上,我想删除可以在grp中找到的所有匹配项,并且没有任何等价in my_data。可悲的是,它在Oracle 10g中不起作用。我尝试对左联接(+)使用旧语法,但是它也不起作用。像这样:

delete grp
from grp,
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 (+)
and my_data.id1 is NULL

A IN子句将在没有多个键的情况下起作用,但是我看不到如何在数据中使用它。那么,还有什么选择呢?

oracle oracle10g
4个回答
16
投票

表和数据:

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.

使用in注意如果子查询中的ID可以为null,请不要使用。 Not innull从不返回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

使用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> 

16
投票

Shannon's solution是必经之路:使用运算符NOT IN(或NOT EXISTS)。

但是您可以在Oracle中删除或更新联接,但是synthax与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

此外,只有在该语句将访问哪个基本行没有歧义的情况下,Oracle才允许您更新联接。特别是,如果连接中一行可能出现两次,则Oracle不会冒险进行更新或删除(该语句将失败)。在这种情况下,只有在my_data(id1, id2, id3, id4).

上存在UNIQUE约束时,删除操作才有效

16
投票

如果要确保删除的内容没有歧义,可以将Vincent's solution更改为:

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
投票

我无法添加评论,因为它需要50次重复,因此我在此处添加了答案。我测试了Vincent从查询中删除的语法,该语法不能让您删除想要的内容,至少在所有delete联接案例中,这并不是常见用法。首先,我使用oracle默认用户scott创建一个表:

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

我想从emp中删除emp1中empno的记录(只是一个简单的测试,所以我从查询中使用了此删除:

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

无论表或连接顺序是什么,无论我在哪里使用子句,SQL都会删除emp1中的相应记录。因此,我认为这种从查询中删除不能让您从指定的表中删除。对于这些情况,循环光标将是更好的方法。

© www.soinside.com 2019 - 2024. All rights reserved.