Oracle SQL: Aktualisieren Sie eine Tabelle mit Daten aus einer anderen Tabelle

251

Tabelle 1:

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

Tabelle 2:

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

Wie führe ich in Oracle SQL eine SQL-Aktualisierungsabfrage aus , mit der Tabelle 1 mit Tabelle 2 aktualisiert nameund descverwendet werden kann id? Das Endergebnis, das ich bekommen würde, ist

Tabelle 1:

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

Die Frage stammt aus der Aktualisierung einer Tabelle mit Daten aus einer anderen , jedoch speziell für Oracle SQL.

Muhd
quelle
2
Mögliches Duplikat der SQL-Update-Abfrage mit Daten aus einer anderen Tabelle
p.campbell
Sie müssen zu Ihrer anderen Frage zurückkehren, diese Antwort nicht akzeptieren und ausdrücklich angeben, dass Sie die Oracle PLSQL-Syntax benötigen.
p.campbell
3
@ p.campbell, das ist nicht meine Frage ...
Muhd
1
Oh ich verstehe. Sie haben also den Fragentext kopiert, aber so geändert, dass er das Oracle-Bit enthält.
p.campbell
2
Ja. Und dies ist wahrscheinlich nicht das beste Beispiel, da "desc" ein reserviertes Wort ist, aber na ja.
Muhd

Antworten:

512

Dies wird als korreliertes Update bezeichnet

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 )

Angenommen, die Verknüpfung führt zu einer schlüsselerhaltenen Ansicht

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
Justin Cave
quelle
8
In Ihrem ersten Codebeispiel: Ist die äußere WHERE-Klausel für korrekte Ergebnisse erforderlich? Oder verwenden Sie es nur, um die Abfrage zu beschleunigen?
Mathias Bader
41
@totoro - Im ersten Beispiel WHERE EXISTSverhindert das, dass Sie eine Zeile in aktualisieren, t1wenn keine übereinstimmende Zeile in vorhanden ist t2. Ohne diese Option wird jede Zeile in t1aktualisiert und die Werte werden auf gesetzt, NULLwenn keine übereinstimmende Zeile in vorhanden ist t2. Das ist im Allgemeinen nicht das, was Sie wollen, also wird das WHERE EXISTSim Allgemeinen benötigt.
Justin Cave
3
Es ist erwähnenswert, dass das SELECT ... FROM t2 Muss zu einer eindeutigen Zeile führen muss. Dies bedeutet, dass Sie für alle Felder, die einen eindeutigen Schlüssel enthalten, auswählen müssen - ein nicht eindeutiger Primärschlüssel reicht nicht aus. Ohne Eindeutigkeit werden Sie auf so etwas wie die @ PaulKarr-Schleife reduziert - und wenn es keine eindeutige Korrelation gibt, kann mehr als eine Zielzeile für jede Quellzeile aktualisiert werden.
Andrew Leach
2
Erläuterung zur Schlüsselerhaltungspflicht für aktualisierbare Joins: asktom.oracle.com/pls/asktom/…
Vadzim
1
@RachitSharma - Das bedeutet, dass Ihre Unterabfrage (die Abfrage von table2) mehrere Zeilen für einen oder mehrere table1Werte zurückgibt und Oracle nicht weiß, welche Sie verwenden möchten. Normalerweise bedeutet dies, dass Sie die Unterabfrage so verfeinern müssen, dass sie eine einzelne eindeutige Zeile zurückgibt.
Justin Cave
132

Versuche dies:

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;
Adrian
quelle
4
Sehr schnell, 1159477 Zeilen in 15,5s zusammengeführt
jefissu
3
Ich hoffe, dass jeder, der diese Frage nach 2015 besucht, diese Antwort bemerkt. Beachten Sie, dass dies auch funktioniert, wenn table1und table2es sich um dieselbe Tabelle handelt. Achten Sie einfach auf den -part ONund die WHERE-klausel für die SELECT-Statement von table2!
sjngm
1
Ich finde, dass ich jedes Mal, wenn ich eine weitere Zusammenführung durchführen muss, auf diese Antwort zurückkomme, um mich inspirieren zu lassen. Ich könnte es ausdrucken und an meine Wand
rahmen
Funktioniert wie Charme !! Vielen Dank!
Davidwillianx
WÄHLEN SIE UNTERSCHIEDLICHE ID, FELD1, FELD1 AUS Tabelle2, WO ID NICHT NULL IST
Joseph Poirier
17

Versuchen

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);
Yahia
quelle
4
Der Nachteil dabei ist, dass die SELECT-Anweisung dreimal wiederholt wird. In komplexen Beispielen kann das ein Deal-Breaker sein.
David Balažic
9
Update table set column = (select...)

hat bei mir nie funktioniert, da set nur 1 Wert erwartet - SQL-Fehler: ORA-01427: Einzeilige Unterabfrage gibt mehr als eine Zeile zurück.

Hier ist die Lösung:

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;

So genau führen Sie es im SQLDeveloper-Arbeitsblatt aus. Sie sagen, es ist langsam, aber das ist die einzige Lösung, die in diesem Fall für mich funktioniert hat.

Pau Karr
quelle
Kann jemand bitte erklären, warum dies eine -2 auf Ruf verdient? LOL.
Pau Karr
13
Ich habe die Rate nicht gesenkt, aber es ist keine gute Lösung. Erstens: Wenn die Unterauswahl mehrere Werte zurückgegeben hat, überschreibt die for-Schleife den Namen in Tabelle2 für einige / alle Datensätze mehrmals (nicht sauber). Zweitens: Es gibt keine Order by-Klausel, so dass dies auf unvorhersehbare Weise geschieht (dh der letzte Wert in ungeordneten Daten gewinnt). Drittens: Es wird viel langsamer sein. Unter der Annahme , das Ergebnis der for - Schleife bestimmt war, konnte die ursprüngliche subselect in irgendeiner Art und Weise kontrolliert neu geschrieben wurden nur 1 Wert für jeden Datensatz zurückzukehren ... einfachste gekünstelt Weg wäre (wählen min (name) ...)
Generator
Das war genau das, was ich brauchte. Danke (+1)
Robert Hyatt
3
Wenn Ihre Unterabfrage mehrere Werte enthält, können Sie die Abfrage überdenken und DISTINCT oder GROUP BY mit MIN, MAX verwenden. Nur eine Idee.
Francis
Lange Rede, kurzer Sinn: Wenn Sie dies überhaupt vermeiden können, verwenden Sie NIEMALS irgendeine Art von LOOP in einer T-SQL-Anweisung. Persönlich, wenn es nicht für 0,001% der Zeit war, in denen es keine andere Lösung gibt, denke ich nicht einmal, dass es sogar eine verfügbare Funktion in T-SQL sein sollte. T-SQL ist satzbasiert und funktioniert daher mit ganzen Datensätzen als Ganzes. Es sollte NICHT verwendet werden, um Daten Zeile für Zeile zu bearbeiten.
Ray K.
8

Hier scheint eine noch bessere Antwort mit der 'in'-Klausel zu sein, die mehrere Schlüssel für den Join zulässt :

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

Das Rindfleisch besteht darin, die Spalten, die Sie als Schlüssel in Klammern verwenden möchten, in der where-Klausel vor 'in' zu haben und die select-Anweisung mit denselben Spaltennamen in Klammern zu haben. wo ( Spalte1, Spalte2 ) in ( wählen Sie ( Spalte1, Spalte2 ) aus der Tabelle, in der "die Menge, die ich will" );

Ameise
quelle
Link ist abgelaufen. ( 404)
Dumbo
-3

Wenn Ihre Tabelle t1 und ihre Sicherung t2 viele Spalten haben, können Sie dies auf kompakte Weise tun.

Außerdem bestand mein damit verbundenes Problem darin, dass nur einige der Spalten geändert wurden und viele Zeilen keine Änderungen an diesen Spalten aufwiesen. Daher wollte ich diese in Ruhe lassen - im Grunde genommen eine Teilmenge von Spalten aus einer Sicherung der gesamten Tabelle wiederherstellen. Wenn Sie nur alle Zeilen wiederherstellen möchten, überspringen Sie die where-Klausel.

Der einfachere Weg wäre natürlich, als Auswahl zu löschen und einzufügen, aber in meinem Fall brauchte ich eine Lösung mit nur Updates.

Der Trick ist, dass, wenn Sie * aus einem Tabellenpaar mit doppelten Spaltennamen auswählen, die zweite Tabelle _1 erhält. Also hier ist, was ich mir ausgedacht habe:

  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, ...
Jim P.
quelle
Dies funktioniert bei mir in Oracle 11g nicht. Können Sie ein funktionierendes Beispiel für diese Methode erstellen?
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;
Avila Theresa
quelle