Zaktualizuj instrukcję z wewnętrznym złączeniem w Oracle

298

Mam zapytanie, które działa dobrze w MySQL, ale kiedy uruchamiam je na Oracle, pojawia się następujący błąd:

Błąd SQL: ORA-00933: Niepoprawnie zakończone polecenie SQL
00933. 00000 - „Niepoprawnie zakończone polecenie SQL”

Zapytanie to:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';
użytkownik169743
źródło
Kiedy próbowałem skonfigurować table2 w Oracle w celu przetestowania mojej odpowiedzi, okazało się, że Oracle odrzuciło DESC jako nazwę kolumny.
Janek Bogucki
Przepraszam, właśnie
skróciłem

Odpowiedzi:

412

Ta składnia nie jest poprawna w Oracle. Możesz to zrobić:

UPDATE table1 SET table1.value = (SELECT table2.CODE
                                  FROM table2 
                                  WHERE table1.value = table2.DESC)
WHERE table1.UPDATETYPE='blah'
AND EXISTS (SELECT table2.CODE
            FROM table2 
            WHERE table1.value = table2.DESC);

Lub możesz to zrobić:

UPDATE 
(SELECT table1.value as OLD, table2.CODE as NEW
 FROM table1
 INNER JOIN table2
 ON table1.value = table2.DESC
 WHERE table1.UPDATETYPE='blah'
) t
SET t.OLD = t.NEW

Zależy to od tego, czy widok wbudowany jest uważany przez Oracle za aktualizowalny (możliwość aktualizacji drugiego wyrażenia zależy od niektórych reguł wymienionych tutaj ).

Tony Andrews
źródło
5
Zrobiłem drugi przykład, ale musiałem dodać aliasy do nazw kolumn w zaznaczeniu, a następnie odwołać się do nich po ich nazwach w zestawie, ale zadziałało, dzięki
Gustavo Rubio
41
Drugi przykład ma tę zaletę, że pozwala przetestować SQL przed faktycznym wykonaniem aktualizacji.
Daniel Reis,
10
Drugi przykład działał dla mnie. Podoba mi się ten, ponieważ wygląda na czysty i czytelny. Nie wiem, jakie są zalety i wady między nimi, jeśli chodzi o wydajność. Ale nie martwiłem się tym na razie, ponieważ użyłem tego do jednorazowego skryptu, aby poprawić złe dane.
nemo
5
Drugi pracował dla mnie :). Wyrocznia jest jednym silnym, ale dziwnym zwierzęciem: /
elrado
10
Wyjaśnienie dotyczące zachowanego klucza dla aktualizowanych połączeń: asktom.oracle.com/pls/asktom/…
Vadzim
202

Użyj tego:

MERGE
INTO    table1 trg
USING   (
        SELECT  t1.rowid AS rid, t2.code
        FROM    table1 t1
        JOIN    table2 t2
        ON      table1.value = table2.DESC
        WHERE   table1.UPDATETYPE='blah'
        ) src
ON      (trg.rowid = src.rid)
WHEN MATCHED THEN UPDATE
    SET trg.value = code;
Quassnoi
źródło
2
Działa idealnie, ale Oracle wymagało ode mnie, aby powiedzieć coś merge into table 1 ttakiego.
Michael-O,
1
Późno na imprezę, ale to wciąż dobry wątek. Muszę wiedzieć, czy ... czy coś mi umknęło? Tabela główna „tabela 1”. W USING tabela 1 jest aliasowana jako t1. Tabela 2, z aliasem jako t2, ale w ON referencje to ...? Tabela zewnętrzna 1 - nie t1 - czy jest to odniesienie do tabeli zewnętrznej lub typu? Tabela 2? Nie t2? Je suis zmieszany. Fan lepszych aliasów ...
Marc
Tylko jeden punkt tutaj, jeśli twój klucz (trg.rowid lub src.rid) ma jeden zduplikowany element, ta klauzula zgłasza
Henrique
@Marc W ON, trgjest alias dla tabeli głównej (tabela table1„zewnętrzna” według twojej logiki) i srcodwołuje się do USINGgrupy („tabela wewnętrzna” według twojej logiki). Ale tak, prawdopodobnie można by się do niego lepiej odwoływać, ale udało mi się go śledzić.
vapcguy
1
@ supernova: odpowiedź Tony'ego aktualizuje widok wbudowany. Może to działać w niektórych przypadkach, ale widok musi być „zachowany z kluczem” (każda połączona tabela musi być połączona na równości na swoim kluczu podstawowym lub innym unikalnym zestawie pól). Dzięki temu każdy rekord w tabeli docelowej przyczynia się do co najwyżej jednego rekordu w wynikowym zestawie wierszy, a zatem każdy rekord w tabeli docelowej jest aktualizowany maksymalnie raz.
Quassnoi
25

MERGEz WHEREklauzulą:

MERGE into table1
USING table2
ON (table1.id = table2.id)
WHEN MATCHED THEN UPDATE SET table1.startdate = table2.start_date
WHERE table1.startdate > table2.start_date;

Potrzebujesz WHEREklauzuli, ponieważ ONnie można zaktualizować kolumn, do których odwołuje się klauzula.

Roland
źródło
Ta wersja jest prawdopodobnie czystsza, ale nie jest przyjazna dla wyzwalaczy, ponieważ nie ma możliwości uniknięcia wyzwalaczy aktualizacji dla niezmienionych wierszy przy użyciu tej składni. (Zakładam, że potrzebne są wyzwalacze dla zmienionych wierszy.)
sf_jeff,
14
 UPDATE ( SELECT t1.value, t2.CODE
          FROM table1 t1
          INNER JOIN table2 t2 ON t1.Value = t2.DESC
          WHERE t1.UPDATETYPE='blah')
 SET t1.Value= t2.CODE
Morten Anderson
źródło
11

Nie używaj niektórych z powyższych odpowiedzi.

Niektórzy sugerują użycie zagnieżdżonego WYBIERZ, nie rób tego, jest niesamowicie powolny. Jeśli masz wiele rekordów do zaktualizowania, użyj dołączenia, więc coś takiego:

update (select bonus 
        from employee_bonus b 
        inner join employees e on b.employee_id = e.employee_id 
        where e.bonus_eligible = 'N') t
set t.bonus = 0;

Zobacz ten link, aby uzyskać więcej informacji. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Upewnij się także, że na wszystkich dołączanych stołach znajdują się klucze podstawowe.

duvo
źródło
7

Jak wskazano tutaj , ogólna składnia pierwszego rozwiązania zaproponowanego przez Tony'ego Andrewsa to:

update some_table s
set   (s.col1, s.col2) = (select x.col1, x.col2
                          from   other_table x
                          where  x.key_value = s.key_value
                         )
where exists             (select 1
                          from   other_table x
                          where  x.key_value = s.key_value
                         )

Myślę, że jest to interesujące, szczególnie jeśli chcesz zaktualizować więcej niż jedno pole.

Alexis Dufrenoy
źródło
To mi nie działa. Aktualizuje cały stół.
Natassia Tavares
3

Poniższa składnia działa dla mnie.

UPDATE
(SELECT A.utl_id,
    b.utl1_id
    FROM trb_pi_joint A
    JOIN trb_tpr B
    ON A.tp_id=B.tp_id Where A.pij_type=2 and a.utl_id is null
)
SET utl_id=utl1_id;
Hemant
źródło
@JimGarrison Ponownie edytuj tę odpowiedź, abym mógł usunąć głos negatywny ... Próbowałem użyć tej składni i nie aktualizowałem mojej tabeli. Dowiedziałem się, dlaczego - SETrobiłem a REPLACEi próbowałem usunąć konkretny ciąg w kolumnie - okazuje się, że Oracle traktuje ''jako null, a tego pola nie można było zerować. Myślałem, że składnia po prostu aktualizuje tabelę tymczasową zamiast prawdziwej, ale się myliłem.
vapcguy
2

Używanie opisu zamiast desc dla table2,

update
  table1
set
  value = (select code from table2 where description = table1.value)
where
  exists (select 1 from table2 where description = table1.value)
  and
  table1.updatetype = 'blah'
;
Janek Bogucki
źródło
dlaczego chcesz odpalić dwa osobne zapytania na
table2
2

Działa dobrze wyrocznia

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary
użytkownik5299305
źródło
Można ustawić wiele właściwości, dodając przecinek na końcu tego. Musiałem zrobić t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNamew tabeli po dopasowaniu jej w kolumnie „UserName” ( t1.UserName = t2.UserName), aby pobrać ich nazwę z tabeli o nazwie UserInfo ( select * from UserInfo) t2). Baza danych była taka, że ​​używała UserName jako klucza podstawowego do UserInfo wszędzie, zamiast umieszczać FirstName i LastName bezpośrednio w tabeli. To naprawiło to!
vapcguy
Ta odpowiedź nic nie dodaje do odpowiedzi udzielonej przez Quassnoi pięć lat wcześniej.
Pasza
0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';
afnhsn
źródło
0
UPDATE IP_ADMISSION_REQUEST ip1
SET IP1.WRIST_BAND_PRINT_STATUS=0
WHERE IP1.IP_ADM_REQ_ID        =
  (SELECT IP.IP_ADM_REQ_ID
  FROM IP_ADMISSION_REQUEST ip
  INNER JOIN VISIT v
  ON ip.ip_visit_id=v.visit_id
  AND v.pat_id     =3702
  ); `enter code here`
użytkownik5685973
źródło
0

Dla kompletności, a ponieważ mówimy o Wyroczni, można to również zrobić:

declare
begin
  for sel in (
    select table2.code, table2.desc
    from table1
    join table2 on table1.value = table2.desc
    where table1.updatetype = 'blah'
  ) loop
    update table1 
    set table1.value = sel.code
    where table1.updatetype = 'blah' and table1.value = sel.desc;    
  end loop;
end;
/
Edu Castrillon
źródło
1
Może to zrobić, ale jest to najwolniejszy możliwy sposób.
APC
-1
UPDATE (SELECT T.FIELD A, S.FIELD B
FROM TABLE_T T INNER JOIN TABLE_S S
ON T.ID = S.ID)
SET B = A;

A i B to pola aliasów, nie musisz wskazywać tabeli.

Dan Anderson
źródło
1
Cześć Dan. Piszesz na dość stare pytanie, które ma już bardzo dobre odpowiedzi. Czy potrafisz wyjaśnić, kiedy twoje pytanie jest lepsze niż inne rozwiązania?
Noel Widmer
1
Oczywiście widziałem odpowiedź, w której b = a zostały napisane przez wskazanie nazwy tabeli (table1.B = table2.A), ale nie ma potrzeby wskazywania tabeli.
Dan Anderson
W rzeczywistości aktualizujesz pola z widoku, które są mapowane do tabeli. Gdyby widok wewnętrzny był aliasowany h, wówczas wersją „samodokumentującą” byłoby „set hb = ha”.
sf_jeff
-4
update table1  a 
   set a.col1='Y' 
 where exists(select 1 
                from table2 b
               where a.col1=b.col1 
                 and a.col2=b.col2
             )
test
źródło