Pergunta

Eu estou tentando fazer a seguinte atualização no 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 é a chave primária, voyage_id é uma chave estrangeira. Eu estou tentando atribuir um número sequencial com base nas datas dentro de cada viagem.

No entanto, o acima falhar com ORA-01732: operação de manipulação de dados não é legal nesta visão

O que é o problema e como posso evitá-lo?

Foi útil?

Solução

Uma vez que você não pode atualizar subconsultas com row_number, você vai ter que calcular o número da linha na parte set da atualização. No começo eu tentei isso:

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
)

Mas isso não funciona, porque a subconsulta seleciona apenas uma linha, e então o row_number() é sempre 1. Usando outro subconsulta permite um 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
)

Ele funciona, mas mais complexo do que eu esperaria para essa tarefa.

Outras dicas

Você pode atualizar alguns pontos de vista, mas há restrições e uma delas é que a vista não deve conter funções analíticas. Consulte SQL Language Reference em ATUALIZAÇÃO e procurar primeira ocorrência de "analítica".

Este trabalho vontade, desde há visitas viagem mais de uma porta no mesmo dia (ou as datas incluem um componente de tempo que os torna ú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
)

Eu acho que isso alças o caso em que um visitas Voyage mais de 1 porta por dia e não há nenhum componente de tempo (embora a sequência dos portos visitados no mesmo dia é, então, arbitrária):

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

Não pense que você pode atualizar uma tabela derivada, eu reescrever 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

O primeiro token após a atualização deve ser o nome da tabela para atualização, em seguida, suas colunas-to update. Eu não tenho certeza o que você está tentando alcançar com a instrução SELECT onde ele está, mas você pode' Atualização do conjunto de resultados a partir da escolha legalmente.
A versão do SQL, adivinhar o que você tem em mente, pode parecer ...

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

NOTA: para usar uma instrução SELECT para definir um valor como este que você deve certificar-se apenas 1 linha será retornado do select

EDIT: declaração modificado acima para refletir o que eu estava tentando explicar. A pergunta foi respondida muito bem por Andomar acima

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top