Update basierend auf Unterabfrage schlägt fehl
Frage
Ich versuche, das folgende Update in Oracle 10gR2 zu tun:
update
(select voyage_port_id, voyage_id, arrival_date, port_seq,
row_number() over (partition by voyage_id order by arrival_date) as new_seq
from voyage_port) t
set t.port_seq = t.new_seq
Voyage_port_id ist der Primärschlüssel, voyage_id ein Fremdschlüssel ist. Ich versuche, eine Sequenznummer zugewiesen werden, basierend auf den Daten innerhalb jeder Reise.
schlägt jedoch fehl, die oben mit ORA-01732: Datenmanipulationsoperation nicht legal auf dieser Ansicht
Was ist das Problem und wie kann ich es vermeiden?
Lösung
Da Sie nicht Subqueries mit row_number
aktualisieren können, müssen Sie die Zeilennummer in dem set
Teil des Updates berechnen müssen. Zuerst habe ich versucht, dies:
update voyage_port a
set a.port_seq = (
select
row_number() over (partition by voyage_id order by arrival_date)
from voyage_port b
where b.voyage_port_id = a.voyage_port_id
)
Aber das funktioniert nicht, weil die Unterabfrage nur eine Zeile auswählt und dann die row_number()
ist immer 1. ein andere Unterabfrage verwenden, kann ein sinnvolles Ergebnis:
update voyage_port a
set a.port_seq = (
select c.rn
from (
select
voyage_port_id
, row_number() over (partition by voyage_id
order by arrival_date) as rn
from voyage_port b
) c
where c.voyage_port_id = a.voyage_port_id
)
Es funktioniert, aber komplexer als ich für diese Aufgabe erwarten würde.
Andere Tipps
Sie können einige Ansichten aktualisieren, aber es gibt Einschränkungen und ist, dass die Sicht nicht analytische Funktionen enthalten. Siehe SQL-Sprachreferenz auf UPDATE und die Suche nach ersten Auftreten von „analytischen“.
Das wird funktionieren, sofern keine Reise Besuche mehr als einen Port am selben Tag (oder die Daten umfassen eine Zeitkomponente, die sie einzigartig macht):
update voyage_port vp
set vp.port_seq =
( select count(*)
from voyage_port vp2
where vp2.voyage_id = vp.voyage_id
and vp2.arrival_date <= vp.arrival_date
)
Ich denke, das den Fall behandelt, wo eine Reise Besuche mehr als 1 Port pro Tag und es gibt keine Zeitkomponente (obwohl die Reihenfolge der Häfen am selben Tag besucht ist dann frei wählbar):
update voyage_port vp
set vp.port_seq =
( select count(*)
from voyage_port vp2
where vp2.voyage_id = vp.voyage_id
and (vp2.arrival_date <= vp.arrival_date)
or ( vp2.arrival_date = vp.arrival_date
and vp2.voyage_port_id <= vp.voyage_port_id
)
)
Denken Sie nicht, Sie eine abgeleitete Tabelle aktualisieren kann, würde ich umschreiben als:
update voyage_port
set port_seq = t.new_seq
from
voyage_port p
inner join
(select voyage_port_id, voyage_id, arrival_date, port_seq,
row_number() over (partition by voyage_id order by arrival_date) as new_seq
from voyage_port) t
on p.voyage_port_id = t.voyage_port_id
Der erste Token nach dem Update sollte der Name der Tabelle zu aktualisieren, dann Spalten-to-Update. Ich bin mir nicht sicher, was Sie mit der Select-Anweisung zu erreichen versuchen, wo es ist, aber man kann‘ Update die Ergebnismenge aus der Auswahl rechtlich.
Eine Version des SQL, erraten, was Sie im Kopf haben, aussehen könnte ...
update voyage_port t
set t.port_seq = (<select statement that generates new value of port_seq>)
Hinweis: eine select-Anweisung zu verwenden, ein Wert, wie diese stellen Sie sicher, dass nur 1 Zeile aus der Auswahl zurückgegeben werden machen müssen
EDIT: modifizierte Erklärung oben zu reflektieren, was ich zu erklären versuche. Die Frage wurde oben
sehr schön von Andomar beantwortet