Eine DML-Tabellenausdrucksklausel ist nur dann sinnvoll, wenn Sie Spalten aus mehr als einer Tabelle benötigen. In Ihrem Fall können Sie ein regelmäßiges Update mit einem EXISTS
verwenden :
update web_userrole
set role = replace(role, 'FULL', 'READ')
where read_only <> 'Y'
and exists
(
select 1/0
from web_userdatasource
where datasource = p_datasource
and username = web_userrole.username
);
Wenn Sie wirklich Spalten aus beiden Tabellen verwenden müssen, haben Sie drei Möglichkeiten:
- Wiederholen Sie den Join im
SET
und dasWHERE
Klausel. Das ist einfach zu bauen, aber nicht optimal. - DML-Tabellenausdruck. Das sollte funktionieren, wenn Sie die richtigen Indizes haben.
-
MERGE
, unten ist ein Beispiel.merge into web_userrole using ( select distinct username from web_userdatasource where datasource = p_datasource ) web_userdatasource on ( web_userrole.username = web_userdatasource.username and web_userrole.read_only <> 'Y' ) when matched then update set role = replace(role, 'FULL', 'READ');
Dies beantwortet Ihre Frage nicht direkt, sondern bietet stattdessen einige Problemumgehungen. Ich kann den Fehler, den du bekommst, nicht reproduzieren. Ich brauche einen vollständigen Testfall, um es weiter zu untersuchen.
Allgemeine Hinweise für aktualisierbare Ansichten
Eines der Hauptprobleme bei aktualisierbaren Ansichten ist die große Anzahl von Einschränkungen hinsichtlich der Abfragen, die sie enthalten können. Die Abfrage oder Ansicht darf nicht viele Merkmale enthalten, wie z. B. DISTINCT, GROUP BY, bestimmte Ausdrücke usw. Abfragen mit diesen Merkmalen können die Ausnahme "ORA-01732:data manipulation operation not legal on this view" auslösen.
Die aktualisierbare Ansichtsabfrage muss jede Zeile der geänderten Tabelle nur einmal eindeutig zurückgeben. Die Abfrage muss „schlüsselerhalten“ sein, was bedeutet, dass Oracle in der Lage sein muss, einen Primärschlüssel oder eine eindeutige Einschränkung zu verwenden, um sicherzustellen, dass jede Zeile nur einmal geändert wird.
Um zu demonstrieren, warum die Beibehaltung des Schlüssels wichtig ist, erstellt der folgende Code eine mehrdeutige Update-Anweisung. Es werden zwei Tabellen erstellt, die erste Tabelle hat eine Zeile und die zweite Tabelle hat zwei Zeilen. Die Tabellen werden durch die Spalte A
verbunden , und versuchen Sie, die Spalte B
zu aktualisieren in der ersten Tabelle. In diesem Fall ist es gut, dass Oracle die Aktualisierung verhindert, da der Wert sonst nicht deterministisch wäre. Manchmal wurde der Wert auf "1" gesetzt, manchmal auf "2".
--Create table to update, with one row.
create table test1 as
select 1 a, 1 b from dual;
--Create table to join two, with two rows that match the other table's one row.
create table test2 as
select 1 a, 1 b from dual union all
select 1 a, 2 b from dual;
--Simple view that joins the two tables.
create or replace view test_view as
select test1.a, test1.b b_1, test2.b b_2
from test1
join test2 on test1.a = test2.a;
--Note how there's one value of B_1, but two values for B_2.
select *
from test_view;
A B_1 B_2
- --- ---
1 1 1
1 1 2
--If we try to update the view it fails with this error:
--ORA-01779: cannot modify a column which maps to a non key-preserved table
update test_view
set b_1 = b_2;
--Using a subquery also fails with the same error.
update
(
select test1.a, test1.b b_1, test2.b b_2
from test1
join test2 on test1.a = test2.a
)
set b_1 = b_2;
Das MERGE
-Anweisung hat nicht die gleichen Einschränkungen. Das MERGE
-Anweisung scheint zu versuchen, Mehrdeutigkeiten zur Laufzeit statt zur Kompilierzeit zu erkennen.
Leider MERGE
leistet nicht immer gute Arbeit bei der Erkennung von Mehrdeutigkeiten. Unter Oracle 12.2 funktioniert die folgende Anweisung gelegentlich und schlägt dann fehl. Kleine Änderungen an der Abfrage können dazu führen, dass sie funktioniert oder fehlschlägt, aber ich kann kein bestimmtes Muster finden.
--The equivalent MERGE may work and changes "2" rows, even though there's only one.
--But if you re-run, or uncomment out the "order by 2 desc" it might raise:
-- ORA-30926: unable to get a stable set of rows in the source tables
merge into test1
using
(
select test1.a, test1.b b_1, test2.b b_2
from test1
join test2 on test1.a = test2.a
--order by 2 desc
) new_rows
on (test1.a = new_rows.a)
when matched then update set test1.b = new_rows.b_2;
UPDATE
schlägt zur Kompilierzeit fehl, wenn es theoretisch möglich ist, Duplikate zu haben. Einige Aussagen, die sollten Arbeit wird nicht ausgeführt.
MERGE
schlägt fehl, wenn die Datenbank zur Laufzeit instabile Zeilen erkennt. Einige Aussagen, die nicht sollten Arbeit wird trotzdem ausgeführt.