Actualización basada en la subconsulta produce un error
Pregunta
Estoy tratando de hacer la siguiente actualización de 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 es la clave principal, voyage_id es una clave externa.Estoy tratando de asignar un número de secuencia basado en las fechas dentro de cada viaje.
Sin embargo, lo anterior no con ORA-01732:manipulación de datos de la operación no es legal en este punto de vista
¿Cuál es el problema y cómo puedo evitarlo ?
Solución
Como no se puede actualizar con subconsultas row_number
, que tendrá que calcular el número de fila en la parte set
de la actualización. Al principio traté de esto:
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
)
Pero eso no funciona, debido a que la subconsulta sólo selecciona una fila, y luego el row_number()
es siempre 1. El uso de otro sub consulta permite un resultado 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
)
Funciona, pero más complejo de lo que cabría esperar para esta tarea.
Otros consejos
Puede actualizar algunos puntos de vista, pero hay restricciones y una es que la vista no debe contener funciones analíticas. Ver SQL Referencia del lenguaje de ACTUALIZACIÓN y la búsqueda de la primera aparición de "analítica".
Esto funcionará, siempre y cuando no visitas Viaje de más de un puerto en el mismo día (o la fecha incluyen un componente de tiempo que los hace únicos):
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
)
Creo que maneja el caso en que un viaje visitas más de 1 puerto por día y no hay componente de tiempo (aunque la secuencia de los puertos visitados en el mismo día es entonces 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
)
)
No creo que se puede actualizar una tabla derivada, me gustaría volver a escribir como:
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
El primer símbolo después de la actualización debe ser el nombre de la tabla para actualizar, a continuación, sus columnas-a-actualización. No estoy seguro de lo que está tratando de lograr con la instrucción de selección donde está, pero se puede' actualización el conjunto de resultados de la selecta legalmente.
Una versión del SQL, adivinando lo que tiene en mente, puede ser como ...
update voyage_port t
set t.port_seq = (<select statement that generates new value of port_seq>)
Nota: para utilizar una instrucción de selección para establecer un valor como éste debe asegurarse de que sólo 1 fila se devuelve desde el selecto
EDIT: modificado declaración anterior para reflejar lo que estaba tratando de explicar. La pregunta se ha respondido muy bien por encima Andomar