web-dev-qa-db-ja.com

Oracle10gの左結合で削除

MS SQLServerで正常に機能する次のコードがあります。

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。残念ながら、Oracle10gでは機能しません。左結合(+)に古い構文を使用してみましたが、どちらも機能しません。このような:

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

IN句は、複数のキーがない場合は機能しますが、データでどのように使用できるかわかりません。それで、代替手段は何ですか?

15
Simon T.

表とデータ:

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 in of nullが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> 
15

削除される内容にあいまいさがないことを確認したい場合は、 Vincentのソリューション を次のように変更できます。

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
    )
16
Nick Pierpoint

シャノンの解決策 行く方法です:演算子NOT IN(またはNOT EXISTS)を使用してください。

ただし、Oracleで結合を削除または更新することはできますが、構文はMS SQLServerと同じではありません。

_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は、ステートメントがどの基本行にアクセスするかについて曖昧さがない場合にのみ、結合を更新できるようにします。特に、行が結合に2回表示される可能性がある場合、Oracleは更新または削除のリスクを負いません(ステートメントは失敗します)。この場合、削除はmy_data(id1, id2, id3, id4).にUNIQUE制約がある場合にのみ機能します。

15
Vincent Malgrat

50回の繰り返しが必要なためコメントを追加できないので、ここに回答を追加します。

Vincentのクエリからの削除をテストしましたが、その構文では必要なものを削除できません。少なくとも、すべての削除結合の場合に一般的に使用されるわけではありません。

最初に、Oracleのデフォルトユーザーscottを使用してテーブルを作成します。

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

Emp1のempno(単純なテスト)でempからレコードを削除したいので、次のクエリからの削除を使用しました。

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

テーブルまたは結合の順序が何であれ、左結合または内部結合であっても、where句を使用していても、SQLはemp1の対応するレコードを削除します。

したがって、このクエリからの削除では、指定したテーブルから削除することはできないと思います。このような場合は、カーソルをループする方が適切です。

0
Artisan