L'aggiornamento basato sulla sottoquery non riesce
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?
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