Dichiarazione di aggiornamento con join interno su Oracle


298

Ho una query che funziona bene in MySQL, ma quando la eseguo su Oracle ottengo il seguente errore:

Errore SQL: ORA-00933: comando SQL non terminato correttamente
00933. 00000 - "Comando SQL non terminato correttamente"

La query è:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';

Quando ho provato a installare table2 in Oracle per testare la mia risposta, ho scoperto che Oracle ha rifiutato DESC come nome di colonna.
Janek Bogucki,

Scusate ho appena abbreviato il nome della colonna originale per discenderne ovviamente non quello nel db
user169743

Risposte:


412

Tale sintassi non è valida in Oracle. Puoi farlo:

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);

O potresti essere in grado di farlo:

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

Dipende se la vista inline è considerata aggiornabile da Oracle ( Essere aggiornabili per la seconda istruzione dipende da alcune regole elencate qui ).


5
Ho fatto il secondo esempio, ma ho dovuto aggiungere alias ai nomi delle colonne nella selezione e quindi fare riferimento a loro con i loro nomi nel SET, ma ha funzionato, grazie
Gustavo Rubio,

41
Il secondo esempio ha il vantaggio di consentire di testare l'SQL prima di eseguire effettivamente l'aggiornamento.
Daniel Reis,

10
Il secondo esempio ha funzionato per me. Mi piace quello perché sembra pulito e leggibile. Non so quali sono i vantaggi e gli svantaggi tra i due quando si tratta di prestazioni. Ma non ero preoccupato per quello, per ora, perché l'ho usato per uno script una tantum per correggere dati errati.
nemo,

5
Secondo ha funzionato per me :). Oracle è un animale forte ma strano: /
elrado,

10
Spiegazione sui requisiti conservati per le chiavi per i join aggiornabili: asktom.oracle.com/pls/asktom/…
Vadzim,

202

Usa questo:

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;

2
Funziona perfettamente ma Oracle mi ha richiesto di dirlo merge into table 1 te così via.
Michael-O,

1
In ritardo alla festa, ma questo è ancora un buon filo. Devo sapere, anche se ... mi sono perso qualcosa? Tabella principale, "tabella1". In USING, table1 è alias come t1. Tabella2, aliasata come t2, ma in ON i riferimenti sono ...? Tabella esterna1 - non t1 - è un riferimento alla tabella esterna o a un tipo? Tavolo 2? Non t2? Je suis è confuso. Fan di alias migliori ...
Marc

Solo un punto qui, se la tua chiave (trg.rowid o src.rid) ha un elemento duplicato questa clausola genera un errore: ora-30926.ora-code.com
Henrique

@Marc In ON, trgè l'alias per la tabella principale, table1( tabella "esterna" secondo la logica) e fa srcriferimento al USINGgruppo ("tabella interna" con la logica). Ma sì, probabilmente avrebbe potuto essere indicato meglio, ma sono stato in grado di seguirlo.
vapcguy,

1
@supernova: la risposta di tony sta aggiornando una vista in linea. Questo può funzionare in alcuni casi, ma la vista deve essere "preservata dalla chiave" (ogni tabella unita deve essere unita alla parità sulla sua chiave primaria o altrimenti set di campi univoco). In questo modo ogni record nella tabella di destinazione contribuisce al massimo a un record nel set di righe risultante e, quindi, ogni record nella tabella di destinazione viene aggiornato al massimo una volta.
Quassnoi,

25

MERGEcon WHEREclausola:

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;

È necessaria la WHEREclausola perché le colonne a cui fa riferimento la ONclausola non possono essere aggiornate.


Questa versione è probabilmente più pulita, ma non è facile da attivare perché non sono in alcun modo a conoscenza di ciò che evito di attivare i trigger di aggiornamento per le righe invariate utilizzando questa sintassi. (
Suppongo

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

11

Non utilizzare alcune delle risposte sopra.

Alcuni suggeriscono l'uso di SELECT nidificato, non farlo, è estremamente lento. Se hai molti record da aggiornare, usa join, quindi qualcosa del tipo:

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;

Vedi questo link per maggiori dettagli. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Inoltre, assicurati che ci siano chiavi primarie su tutte le tabelle a cui ti stai unendo.


7

Come indicato qui , la sintassi generale per la prima soluzione proposta da Tony Andrews è:

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
                         )

Penso che questo sia interessante soprattutto se vuoi aggiornare più di un campo.


Questo non funziona per me. Aggiorna l'intera tabella.
Natassia Tavares,

3

Questa sintassi seguente funziona per me.

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;

@JimGarrison Modifica nuovamente questa risposta in modo da poter rimuovere il mio downvote .... Stavo cercando di utilizzare questa sintassi e non stava aggiornando la mia tabella. Ho scoperto perché - SETstavo facendo un REPLACEe stavo cercando di cancellare una particolare stringa nella colonna - ho scoperto che i tratti di Oracle sono ''nulli e questo campo non può essere annullato. Pensavo che la sintassi stesse semplicemente aggiornando una tabella temporanea anziché quella reale, ma mi sbagliavo.
vapcguy,

2

Utilizzando la descrizione anziché la descrizione per 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'
;

perché vuoi lanciare due query separate su table2
Jitendra Vispute,

2

Funziona bene oracolo

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary

È possibile impostare più proprietà aggiungendo una virgola alla fine. Ho dovuto farlo t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNamesu una tabella dopo averlo trovato sulla colonna "UserName" ( t1.UserName = t2.UserName) per recuperare il loro nome da una tabella chiamata UserInfo ( select * from UserInfo) t2). Il database era tale dove utilizzava UserName come chiave primaria di UserInfo ovunque, invece di inserire direttamente FirstName e LastName nella tabella. Questo risolto questo!
vapcguy,

Questa risposta non aggiunge nulla alla risposta già fornita da Quassnoi cinque anni prima della tua.
Foraggio,

0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';

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`

0

Proprio per completezza e poiché stiamo parlando di Oracle, anche questo potrebbe farlo:

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;
/

1
Questo potrebbe farlo, ma è il modo più lento possibile.
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 e B sono campi alias, non è necessario puntare la tabella.


1
Ciao Dan Stai pubblicando una domanda piuttosto vecchia che ha già ottime risposte. Puoi spiegare quando la tua domanda è preferibile rispetto alle altre soluzioni?
Noel Widmer,

1
Ovviamente, ho visto una risposta in cui sono stati scritti b = a indicando il nome della tabella (table1.B = table2.A) ma non è necessario puntare la tabella.
Dan Anderson,

In realtà stai aggiornando i campi dalla vista, che vengono mappati alla tabella. Se la vista interna fosse alias h, la versione "auto-documentante" sarebbe "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
             )
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.