2010-09-08 11 views
13

Mam następujący kod, który działa dobrze w MS SQL Server:Usuń z LEFT JOIN w Oracle 10g

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 

Zasadniczo, chcę usunąć wszystkie wystąpienia, które można znaleźć w grp i nie mają żadnego równoważność in my_data. Niestety, nie działa w Oracle 10g. Próbowałem używać starej składni dla lewego łączenia (+), ale to też nie działa. Tak:

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 Klauzula ta działa, jeśli nie mam wiele kluczy, ale nie rozumiem, jak można go używać z moimi danymi. Czym więc jest alternatywa?

+0

SQL Server wspiera sprzężenia w usuwaniu i aktualizacji to niestandardowe rozszerzenie do SQL. –

+0

Shannon Severance: MySQL too –

Odpowiedz

15

tabele i dane:

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. 

Korzystanie in. Uwaga Nie należy używać, jeśli identyfikatory w podzapytaniu mogą być null. Not in z null nigdy nie zwraca wartości 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 

Stosując 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

Zaimplementowałem wszystkie trzy proponowane rozwiązania. "Gdzie nie istnieje" jest prawie 3 razy szybsze niż rozwiązania "nie w" i "pozostawione" w dokładnie takim przypadku, w którym testowałem je na Oracle. Nie wiem, czy którekolwiek z tych rozwiązań jest zgodne ze standardami SQL, ale rozwiązanie "where not exist" jest jedynym, które działa zarówno na Oracle, jak i na MS SQL Server. Jestem zaskoczony, że rozwiązanie 'not in' może być używane z wieloma polami, ponieważ nie ma wyraźnego powiązania. Czy to działa, ponieważ mają te same nazwy? Czy można to zrobić, używając aliasu? –

+0

'nie w' z wieloma polami jest zdefiniowany w jakimś standardzie SQL (SQL-92?), Ale ostatnio zaznaczone nie zostało zaimplementowane w SQL Server. '(w, x, y, z) w (wybierz a, b, c, d od ....)' bierze krotkę po lewej stronie, '(w, x, y, z)' I sprawdza to przed każdy wiersz (krotka) zwrócona po prawej stronie: 'w = a i x = b oraz y = c i z = d'. Nie zależy od tego, czy nazwy są takie same.) –

15

Shannon's solution jest droga: stosować operator nie (albo nie istnieje).

Można jednak usunąć lub zaktualizować sprzężenia w Oracle, ale synthax nie jest taki sam jak 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 

Dodatkowo Oracle pozwoli tylko zaktualizować sprzężenia jeśli nie ma niejednoznaczność jako do którego wiersz podstawowy będzie dostępny przez wyciąg. W szczególności firma Oracle nie będzie ryzykować aktualizacji ani usunięcia (nie powiedzie się instrukcja), jeśli istnieje możliwość, że wiersz może pojawić się dwa razy w łączeniu. W tym przypadku kasowania będzie działać tylko wtedy, gdy istnieje UNIQUE na my_data(id1, id2, id3, id4).

15

Jeśli chcesz mieć pewność, nie ma wątpliwości w to, co przed usunięciem można zmienić Vincent's solution do:

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 
    )