Aktualisieren Sie die Anweisung mit Inner Join unter Oracle


298

Ich habe eine Abfrage, die in MySQL gut funktioniert, aber wenn ich sie unter Oracle ausführe, wird folgende Fehlermeldung angezeigt:

SQL-Fehler: ORA-00933: SQL-Befehl nicht ordnungsgemäß beendet
00933. 00000 - "SQL-Befehl nicht ordnungsgemäß beendet"

Die Abfrage lautet:

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

Als ich versuchte, table2 in Oracle einzurichten, um meine Antwort zu testen, stellte ich fest, dass Oracle DESC als Spaltennamen ablehnte.
Janek Bogucki

Entschuldigung, ich habe gerade den ursprünglichen Spaltennamen abgekürzt, um ihn offensichtlich nicht in der
Datenbank abzurufen

Antworten:


412

Diese Syntax ist in Oracle nicht gültig. Du kannst das:

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

Oder Sie können dies tun:

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

Dies hängt davon ab, ob die Inline-Ansicht von Oracle als aktualisierbar angesehen wird ( Die Aktualisierung für die zweite Anweisung hängt von einigen hier aufgeführten Regeln ab ).


5
Ich habe das zweite Beispiel gemacht, musste aber Aliase zu den Spaltennamen in der Auswahl hinzufügen und sie dann durch ihre Namen im SET referenzieren, aber es hat funktioniert, danke
Gustavo Rubio

41
Das zweite Beispiel bietet den Vorteil, dass Sie die SQL testen können, bevor Sie das Update tatsächlich durchführen.
Daniel Reis

10
Das zweite Beispiel hat bei mir funktioniert. Ich mag das, weil es sauber und lesbar aussieht. Ich weiß nicht, was die Vor- und Nachteile zwischen den beiden sind, wenn es um Leistung geht. Aber darüber habe ich mir vorerst keine Sorgen gemacht, weil ich dies für ein einmaliges Skript verwendet habe, um fehlerhafte Daten zu korrigieren.
Nemo

5
Zweiter arbeitete für mich :). Oracle ist ein starkes, aber seltsames Tier: /
Elrado

10
Erklärung zur Schlüsselerhaltung für aktualisierbare Joins: asktom.oracle.com/pls/asktom/…
Vadzim

202

Benutze das:

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
Funktioniert perfekt, aber Oracle hat mich aufgefordert, merge into table 1 tdies zu sagen und so weiter.
Michael-O

1
Spät zur Party, aber das ist immer noch ein guter Thread. Ich muss wissen, ob ... habe ich etwas verpasst? Mastertabelle "table1". In USING ist Tabelle1 als t1 aliasiert. Tabelle 2, alias t2, aber im ON sind die Referenzen ...? Externe Tabelle1 - nicht t1 - ist dies ein Verweis auf die äußere Tabelle oder ein Typ? Tabelle 2? Nicht t2? Je suis verwirrt. Fan von besseren Aliasnamen ...
Marc

Nur ein Punkt hier, wenn Ihr Schlüssel (trg.rowid oder src.rid) ein doppeltes Element hat, wirft diese Klausel einen Fehler aus: ora-30926.ora-code.com
Henrique

@Marc In ON, trgist der Alias ​​für die Mastertabelle table1("äußere" Tabelle nach Ihrer Logik) und srcverweist auf die USINGGruppe ("innere Tabelle" nach Ihrer Logik). Aber ja, wahrscheinlich hätte man besser darauf verweisen können, aber ich konnte ihm folgen.
Vapcguy

1
@supernova: tony antwortet mit der aktualisierung einer inline-ansicht. Dies kann in einigen Fällen funktionieren, aber die Ansicht muss "schlüsselerhalten" sein (jede verknüpfte Tabelle muss auf ihrem Primärschlüssel oder einer anderen eindeutigen Feldmenge gleich verknüpft sein). Dadurch wird sichergestellt, dass jeder Datensatz in der Zieltabelle zu höchstens einem Datensatz im resultierenden Rowset beiträgt und daher jeder Datensatz in der Zieltabelle höchstens einmal aktualisiert wird.
Quassnoi

25

MERGEmit WHEREKlausel:

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;

Sie benötigen die WHEREKlausel, da Spalten, auf die in der ONKlausel verwiesen wird , nicht aktualisiert werden können.


Diese Version ist wohl sauberer, aber nicht triggerfreundlich, da mir nicht bekannt ist, dass das Auslösen von Update-Triggern für unveränderte Zeilen mit dieser Syntax vermieden werden kann. (Ich
gehe

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

Verwenden Sie einige der obigen Antworten nicht.

Einige schlagen die Verwendung von verschachteltem SELECT vor, tun Sie das nicht, es ist unerträglich langsam. Wenn Sie viele Datensätze aktualisieren müssen, verwenden Sie join.

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;

Siehe diesen Link für weitere Details. http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx .

Stellen Sie außerdem sicher, dass in allen Tabellen, denen Sie beitreten, Primärschlüssel vorhanden sind.


7

Wie hier angegeben , lautet die allgemeine Syntax für die erste von Tony Andrews vorgeschlagene Lösung:

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
                         )

Ich denke, das ist besonders interessant, wenn Sie mehr als ein Feld aktualisieren möchten.


Das funktioniert bei mir nicht. Es aktualisiert die gesamte Tabelle.
Natassia Tavares

3

Diese folgende Syntax funktioniert für mich.

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 Bitte bearbeiten Sie diese Antwort erneut, damit ich meine Ablehnung entfernen kann. Ich habe versucht, diese Syntax zu verwenden, und meine Tabelle wurde nicht aktualisiert. Ich habe herausgefunden, warum - ich habe SETa ausgeführt REPLACEund versucht, eine bestimmte Zeichenfolge in der Spalte ''zu löschen - Oracle-Behandlungen als null behandelt werden und dieses Feld nicht null gesetzt werden konnte. Ich dachte, die Syntax würde lediglich eine temporäre Tabelle anstelle der echten aktualisieren, aber ich habe mich geirrt.
Vapcguy

2

Verwenden Sie description anstelle von desc für 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'
;

Warum willst du zwei separate Abfragen auf Tabelle 2
abfeuern

2

Es funktioniert gut Orakel

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

Kann mehrere Eigenschaften festlegen, indem am Ende ein Komma hinzugefügt wird. Ich musste t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastNameeine Tabelle bearbeiten, nachdem ich sie in der Spalte "UserName" ( t1.UserName = t2.UserName) abgeglichen hatte , um ihren Namen aus einer Tabelle namens UserInfo ( select * from UserInfo) t2) abzurufen . In der Datenbank wurde UserName überall als Primärschlüssel für UserInfo verwendet, anstatt FirstName und LastName direkt in der Tabelle zu platzieren. Das hat das behoben!
Vapcguy

Diese Antwort fügt nichts zu der Antwort hinzu, die Quassnoi bereits fünf Jahre vor Ihrer Antwort gegeben hat.
Futter

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

Der Vollständigkeit halber und weil es sich um Oracle handelt, könnte dies auch so sein:

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
Dies könnte es tun, aber es geht um den langsamsten Weg, der möglich ist.
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 und B sind Aliasfelder, Sie müssen nicht auf die Tabelle zeigen.


1
Hallo Dan. Sie posten auf eine ziemlich alte Frage, die bereits sehr gute Antworten hat. Können Sie erklären, wann Ihre Frage den anderen Lösungen vorzuziehen ist?
Noel Widmer

1
Natürlich habe ich eine Antwort gesehen, bei der b = a durch Zeigen auf den Tabellennamen (table1.B = table2.A) geschrieben wurde, aber es ist nicht erforderlich, auf die Tabelle zu zeigen.
Dan Anderson

Sie aktualisieren tatsächlich Felder aus der Ansicht, die der Tabelle zugeordnet werden. Wenn die innere Ansicht mit einem Alias ​​versehen wäre, wäre die "selbstdokumentierende" Version "setze 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
             )
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.