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?

War es hilfreich?

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top