it-swarm.com.de

Verwenden einer Unterabfrage anstelle eines Tabellennamens in einer Oracle Update-Anweisung

Ich muss eine Aktualisierungsanweisung schreiben, in der mehrere Tabellen verwendet wurden, um die zu aktualisierenden Zeilen zu bestimmen, da in Oracle nicht mehrere Tabellen zulässig sind. Die folgende Abfrage gibt einen Fehler "ORA-00971: Fehlendes SET-Schlüsselwort" zurück

UPDATE
  TABLE1 a,
  TABLE2 b
SET
  a.COL1 = 'VALUE'
WHERE
  a.FK = b.PK
  AND b.COL2 IN ('SET OF VALUES')

Beim Nachschlagen der UPDATE-Anweisungssyntax unter Oracle fand ich das folgende link , das zeigt, dass Sie anstelle eines Tabellennamens eine Unterabfrage verwenden können.

Als ich versuchte, die Abfrage so zu schreiben, erhielt ich eine "ORA-01779: Eine Spalte kann nicht geändert werden, die einer Tabelle ohne Schlüssel entspricht".

UPDATE
  (
    SELECT
      a.COL1
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
  ) update_tbl
SET
  update_tbl.COL1 = 'VALUE'

Ich habe die Abfrage (siehe unten) mit einer EXISTS-Anweisung neu geschrieben, und sie funktioniert gut, möchte aber trotzdem wissen, wie dies geschieht.

UPDATE
  TABLE1 update_tbl
SET
  update_tbl.COL1 = 'VALUE'
WHERE
  EXISTS (
    SELECT
      1
    FROM
      TABLE1 a
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
      AND update_tbl.PK = a.PK
  )

Danke! - Nate

13
NateSchneider

Andere Option:

UPDATE TABLE1 a
SET a.COL1 = 'VALUE'
WHERE a.FK IN
( SELECT b.PK FROM TABLE2 b
  WHERE b.COL2 IN ('SET OF VALUES')
)

Ihr zweites Beispiel würde funktionieren, wenn (a) die Ansicht die deklarierte PK von TABLE1 enthält:

UPDATE
  (
    SELECT
      a.COL1, a.PKCOL
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
  ) update_tbl
SET
  update_tbl.COL1 = 'VALUE'

... und (b) TABLE1.FK war ein deklarierter Fremdschlüssel für TABLE2

(Mit deklariert meine ich, dass eine Einschränkung existiert und aktiviert ist).

7
Tony Andrews

Ich finde, dass eine schnelle, konsistente Methode zur Umwandlung einer SELECT-Anweisung in ein UPDATE darin besteht, das Update anhand der ROWID vorzunehmen.

UPDATE
  TABLE1
SET
  COL1 = 'VALUE'
WHERE
  ROWID in
    (
    SELECT
      a.rowid
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
    )

Ihre innere Abfrage definiert also die zu aktualisierenden Zeilen.

6
Nick Pierpoint

Die Syntax Ihres Beispiels ist in Ordnung, aber Oracle erfordert, dass die Unterabfrage Primärschlüssel enthält. Das ist eine ziemlich erhebliche Einschränkung.

In einem verwandten Hinweis können Sie auch Klammern verwenden, um zwei oder mehr Felder in einer IN-Anweisung zu verwenden, wie in:

UPDATE
  TABLE1 update_tbl
SET
  update_tbl.COL1 = 'VALUE'
WHERE
  (update_tbl.PK1, update_tbl.pk2) in(
                      select some_field1, some_field2
                      from some_table st
                      where st.some_fields = 'some conditions'
                      );
3
JosephStyons

Wenn Sie ein Update durchführen, können Sie dem System natürlich nur mitteilen, dass der Wert auf einen einzelnen neuen Wert aktualisiert werden soll. Wenn Sie ein Update auf das Ergebnis einer Inline-Ansicht stützen, prüft Oracle, ob ausreichende Einschränkungen vorhanden sind, um zu verhindern, dass eine geänderte Spalte möglicherweise zweimal aktualisiert wird.

In Ihrem Fall erwarte ich, dass TABLE2.PK eigentlich kein deklarierter Primärschlüssel ist. Wenn Sie dieser Spalte eine primäre oder eindeutige Einschränkung auferlegen, dann sind Sie bereit zu gehen.

Es gibt einen undokumentierten Hinweis, um die Kardinalitätsprüfung für Updates zu umgehen, die intern von Oracle verwendet wird. Ich würde jedoch nicht empfehlen, sie zu verwenden.

Eine Problemumgehung besteht darin, eine MERGE-Anweisung zu verwenden, die nicht demselben Test unterzogen wird.

2
David Aldridge

Ich habe gefunden, was ich hier brauchte: Nützliche SQL-Befehle

Ich musste eine Tabelle mit dem Ergebnis eines Joins aktualisieren
Ich habe die obigen Lösungen ohne Erfolg ausprobiert :(

Hier ist ein Auszug der Seite, die ich oben gezeigt habe
Mit Cursorn konnte ich die Aufgabe erfolgreich lösen
Ich bin sicher, dass es eine andere Lösung gibt, aber diese hat so funktioniert ...

DECLARE

 /* Output variables to hold the result of the query: */
 a T1.e%TYPE;
 b T2.f%TYPE;
 c T2.g%TYPE;

 /* Cursor declaration: */
 CURSOR T1Cursor IS
   SELECT T1.e, T2.f, T2.g
   FROM T1, T2
   WHERE T1.id = T2.id AND T1.e <> T2.f

 FOR UPDATE;

BEGIN

  OPEN T1Cursor;

  LOOP

    /* Retrieve each row of the result of the above query
    into PL/SQL variables: */
    FETCH T1Cursor INTO a, b;

    /* If there are no more rows to fetch, exit the loop: */
    EXIT WHEN T1Cursor%NOTFOUND;

    /* Delete the current Tuple: */
    DELETE FROM T1 WHERE CURRENT OF T1Cursor;

    /* Insert the reverse Tuple: */
    INSERT INTO T1 VALUES(b, a);

    /* Here is my stuff using the variables to update my table */
    UPDATE T2
    SET T2.f = a
    WHERE T2.id = c;

  END LOOP;

  /* Free cursor used by the query. */
  CLOSE T1Cursor;

END;
.
run;


Hinweis: Vergessen Sie nicht zu begehen ;-)

1
Franck

Jede Zeile in der Ergebnismenge der Abfrage in Ihrer UPDATE-Klausel muss einer einzigen Zeile in der Tabelle zugeordnet werden, die Sie aktualisieren möchten, und zwar so, dass Oracle automatisch folgen kann. Da es sich bei der Abfrage wirklich um eine Sicht handelt, besteht eine Möglichkeit, darüber nachzudenken, dass Oracle die Sicht wieder mit der Zieltabelle verbinden muss, um zu wissen, welche Zeile aktualisiert werden soll.

Dies bedeutet im Wesentlichen, dass Sie den Primärschlüssel der Zieltabelle in diese Abfrage aufnehmen müssen. Sie können möglicherweise auch andere eindeutige Indexfelder verwenden, aber ich kann nicht garantieren, dass Oracle DBMS intelligent genug ist, um dies zuzulassen.

0
Chris Ammerman