Domanda

Sto cercando di eseguire il seguente aggiornamento in Oracle 10gR2:

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 è la chiave primaria, Voyage_id è una chiave esterna.Sto cercando di assegnare un numero progressivo in base alle date di ciascun viaggio.

Tuttavia, quanto sopra non riesce ORA-01732:operazione di manipolazione dei dati non legale in questo senso

Qual è il problema e come posso evitarlo?

È stato utile?

Soluzione

Poiché non è possibile aggiornare le sottoquery con row_number, dovrai calcolare il numero di riga nel file set parte dell'aggiornamento.All'inizio ho provato questo:

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
)

Ma ciò non funziona, perché la sottoquery seleziona solo una riga, quindi il file row_number() è sempre 1.L'utilizzo di un'altra sottoquery consente un risultato significativo:

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
)

Funziona, ma è più complesso di quanto mi aspetterei per questo compito.

Altri suggerimenti

È possibile aggiornare alcune visualizzazioni, ma esistono restrizioni e una è che la visualizzazione non deve contenere funzioni analitiche.Vedere Riferimento al linguaggio SQL su UPDATE e cercare la prima occorrenza di "analitico".

Funzionerà, a condizione che nessun viaggio tocchi più di un porto nello stesso giorno (o che le date includano una componente temporale che le renda uniche):

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
)

Penso che questo gestisca il caso in cui un viaggio visita più di 1 porto al giorno e non vi è alcuna componente temporale (sebbene la sequenza dei porti visitati nello stesso giorno sia quindi arbitraria):

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

Non pensare di poter aggiornare una tabella derivata, la riscriverei come:

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

Il primo token dopo UPDATE dovrebbe essere il nome della tabella da aggiornare, quindi le colonne da aggiornare.Non sono sicuro di cosa stai cercando di ottenere con l'istruzione select dove si trova, ma puoi' aggiornamento il risultato impostato dalla selezione legalmente.
Una versione di SQL, indovinando cosa hai in mente, potrebbe assomigliare a...

update voyage_port t
set t.port_seq = (<select statement that generates new value of port_seq>)

NOTA: per utilizzare un'istruzione select per impostare un valore come questo è necessario assicurarsi che venga restituita solo 1 riga dal select!

MODIFICARE :dichiarazione modificata sopra per riflettere ciò che stavo cercando di spiegare.Alla domanda ha risposto molto bene Andomar sopra

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top