Oracle SQL: aggiorna una tabella con i dati di un'altra tabella


251

Tabella 1:

id    name    desc
-----------------------
1     a       abc
2     b       def
3     c       adf

Tavolo 2:

id    name    desc
-----------------------
1     x       123
2     y       345

In Oracle Oracle, come posso eseguire una query di aggiornamento sql in grado di aggiornare la tabella 1 con la tabella 2 namee descutilizzando la stessa id? Quindi il risultato finale che vorrei ottenere è

Tabella 1:

id    name    desc
-----------------------
1     x       123
2     y       345
3     c       adf

La domanda viene presa dall'aggiornamento di una tabella con i dati di un'altra , ma specificamente per Oracle Oracle.



Devi tornare all'altra tua domanda, non accettare quella risposta e dichiarare specificamente che hai bisogno della sintassi Oracle PLSQL.
Campbell

3
@ p.campbell, Questa non è una mia domanda ...
Muhd,

1
Oh, capisco. Quindi hai incollato e copiato il corpo della domanda, ma modificato per includere il bit Oracle.
Campbell

2
Si. E questo probabilmente non è il miglior esempio poiché "desc" è una parola riservata, ma vabbè.
Muhd,

Risposte:


512

Questo si chiama aggiornamento correlato

UPDATE table1 t1
   SET (name, desc) = (SELECT t2.name, t2.desc
                         FROM table2 t2
                        WHERE t1.id = t2.id)
 WHERE EXISTS (
    SELECT 1
      FROM table2 t2
     WHERE t1.id = t2.id )

Supponendo che il risultato del join sia una vista conservata dalla chiave, è possibile anche

UPDATE (SELECT t1.id, 
               t1.name name1,
               t1.desc desc1,
               t2.name name2,
               t2.desc desc2
          FROM table1 t1,
               table2 t2
         WHERE t1.id = t2.id)
   SET name1 = name2,
       desc1 = desc2

8
Nel tuo primo esempio di codice: la clausola WHERE esterna è necessaria per risultati corretti? O lo usi solo per velocizzare la query?
Mathias Bader,

41
@totoro - Nel primo esempio, WHERE EXISTSti impedisce di aggiornare una riga t1se non ci sono righe corrispondenti in t2. Senza di essa, ogni riga in t1verrà aggiornata e i valori verranno impostati su NULLse non vi sono righe corrispondenti in t2. Questo non è generalmente quello che vuoi che accada, quindi WHERE EXISTSè generalmente necessario.
Grotta di Giustino,

3
Vale la pena aggiungere che il SELECT ... FROM t2 must deve risultare in una riga univoca. Ciò significa che devi selezionare su tutti i campi che comprendono una chiave univoca - una chiave primaria non univoca non è sufficiente. Senza unicità, si è ridotti a qualcosa come il ciclo di @ PaulKarr - e se non esiste una correlazione univoca, è possibile aggiornare più di una riga di destinazione per ciascuna riga di origine.
Andrew Leach,

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

1
@RachitSharma - Ciò significa che la tua sottoquery (la query da table2) sta restituendo più righe per uno o più table1valori e Oracle non sa quale vuoi usare. Normalmente, ciò significa che è necessario affinare la subquery in modo che restituisca una singola riga distinta.
Grotta di Giustino

132

Prova questo:

MERGE INTO table1 t1
USING
(
-- For more complicated queries you can use WITH clause here
SELECT * FROM table2
)t2
ON(t1.id = t2.id)
WHEN MATCHED THEN UPDATE SET
t1.name = t2.name,
t1.desc = t2.desc;

4
Davvero molto veloce, 1159477 file si sono fuse in 15,5s
jefissu

3
Spero che tutti coloro che visitano questa domanda dopo il 2015 notino questa risposta. Nota che questo funziona anche se table1e table2sono la stessa tabella, prenditi cura di ON-part e WHERE-clause per la SELECTdichiarazione di table2!
sjngm,

1
Trovo che ogni volta che devo fare un'altra fusione continuo a tornare a questa risposta per ispirazione. Potrei stamparlo e incorniciarlo sul mio muro
arnehehe

Funziona come il fascino !! Grazie!
davidwillianx,

SELEZIONA DISTINCT ID, FIELD1, FIELD1 DALLA tabella2 DOVE L'ID NON È NULL
Joseph Poirier

17

provare

UPDATE Table1 T1 SET
T1.name = (SELECT T2.name FROM Table2 T2 WHERE T2.id = T1.id),
T1.desc = (SELECT T2.desc FROM Table2 T2 WHERE T2.id = T1.id)
WHERE T1.id IN (SELECT T2.id FROM Table2 T2 WHERE T2.id = T1.id);

4
L'aspetto negativo di questo è che l'istruzione SELECT viene ripetuta 3 volte. In esempi complessi che possono essere un problema.
David Balažic,

9
Update table set column = (select...)

non ha mai funzionato per me poiché il set prevede solo 1 valore - Errore SQL: ORA-01427: la subquery a riga singola restituisce più di una riga.

ecco la soluzione:

BEGIN
For i in (select id, name, desc from table1) 
LOOP
Update table2 set name = i.name, desc = i.desc where id = i.id;
END LOOP;
END;

Ecco come lo si esegue esattamente sul foglio di lavoro di SQLDeveloper. Dicono che è lento ma questa è l'unica soluzione che ha funzionato per me in questo caso.


qualcuno può spiegare perché questo merita un -2 sulla reputazione? LOL.
Pau Karr,

13
Non ho minimizzato, ma non è una buona soluzione. In primo luogo: se la sottoselezione restituiva più valori, il ciclo for sovrascriverà il nome su table2 più volte per alcuni / tutti i record (non pulito). In secondo luogo: non esiste una clausola order by in modo che ciò accada in modo imprevedibile (ovvero vince l'ultimo valore in dati non ordinati). Terzo: sarà molto più lento. Supponendo che il risultato del ciclo for fosse previsto, la sottoselezione originale avrebbe potuto essere riscritta in qualche modo controllato per restituire solo 1 valore per ogni record ... il modo più semplice e concepito sarebbe (selezionare min (nome) ...)
Alternatore

Questo era esattamente quello di cui avevo bisogno. Grazie (+1)
Robert Hyatt

3
Se si ottengono più valori nella sottoquery, è possibile ripensare la query e utilizzare DISTINCT o GROUP BY con MIN, MAX. Solo un'idea
Francesco,

Per farla breve: se è possibile evitarlo, mai e poi mai MAI utilizzare alcun tipo di LOOP in un'istruzione T-SQL. Personalmente, se non fosse per lo 0,001% delle volte in cui non esiste altra soluzione, non penso nemmeno che dovrebbe essere una funzione disponibile in T-SQL. T-SQL è progettato per essere basato su set, quindi funziona su interi set di dati nel loro insieme; NON dovrebbe essere usato per lavorare su dati riga per riga.
Ray K.

8

Qui sembra essere una risposta ancora migliore con la clausola 'in' che consente più chiavi per il join :

update fp_active set STATE='E', 
   LAST_DATE_MAJ = sysdate where (client,code) in (select (client,code) from fp_detail
  where valid = 1) ...

La cosa migliore è avere le colonne che vuoi usare come chiave tra parentesi nella clausola where prima di 'in' e avere l'istruzione select con gli stessi nomi di colonna tra parentesi. dove ( colonna1, colonna2 ) in ( seleziona ( colonna1, colonna2 ) dalla tabella dove "l'insieme che voglio" );


Il link è scaduto. ( 404)
Dumbo

-3

Se la tua tabella t1 e il suo backup t2 hanno molte colonne, ecco un modo compatto per farlo.

Inoltre, il mio problema correlato era che solo alcune delle colonne erano state modificate e molte righe non avevano modifiche a queste colonne, quindi volevo lasciarle da sole - fondamentalmente ripristinare un sottoinsieme di colonne da un backup dell'intera tabella. Se si desidera ripristinare solo tutte le righe, saltare la clausola where.

Ovviamente il modo più semplice sarebbe quello di eliminare e inserire come selezionato, ma nel mio caso avevo bisogno di una soluzione con solo aggiornamenti.

Il trucco è che quando selezioni * da una coppia di tabelle con nomi di colonna duplicati, il secondo verrà chiamato _1. Quindi, ecco cosa mi è venuto in mente:

  update (
    select * from t1 join t2 on t2.id = t1.id
    where id in (
      select id from (
        select id, col1, col2, ... from t2
        minus select id, col1, col2, ... from t1
      )
    )
  ) set col1=col1_1, col2=col2_1, ...

Questo non funziona per me in Oracle 11g. Puoi creare un esempio funzionante di questo metodo?
Jon Heller,

-3
BEGIN
For i in (select id, name, desc from table2) 
LOOP
Update table1 set name = i.name, desc = i.desc where id = i.id and (name is null or desc is null);
END LOOP;
END;
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.