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 ?

¿Fue útil?

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top