Обновление на основе подзапроса не выполнено

StackOverflow https://stackoverflow.com/questions/1873561

  •  18-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь сделать следующее обновление в 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 — это первичный ключ, voyage_id — внешний ключ.Я пытаюсь присвоить порядковый номер на основе дат каждого рейса.

Однако вышеизложенное терпит неудачу с ОРА-01732:операция манипулирования данными недопустима в этом представлении

В чем проблема и как ее избежать?

Это было полезно?

Решение

Поскольку вы не можете обновлять подзапросы с помощью row_number, вам придется вычислить номер строки в set часть обновления.Сначала я попробовал это:

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
)

Но это не работает, потому что подзапрос выбирает только одну строку, а затем row_number() всегда 1.Использование другого подзапроса позволяет получить значимый результат:

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
)

Это работает, но сложнее, чем я ожидал для этой задачи.

Другие советы

Вы можете обновить некоторые представления, но существуют ограничения, одно из которых заключается в том, что представление не должно содержать аналитические функции.Видеть Справочник по языку SQL в ОБНОВЛЕНИИ и найдите первое появление слова «аналитический».

Это будет работать при условии, что рейс не заходит более чем в один порт в один и тот же день (или даты включают временной компонент, который делает их уникальными):

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
)

Я думаю, что это подходит для случая, когда рейс посещает более 1 порта в день и нет временной составляющей (хотя последовательность портов, посещаемых в один и тот же день, тогда произвольна):

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

Не думайте, что вы можете обновить производную таблицу, я бы переписал так:

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

Первым токеном после UPDATE должно быть имя обновляемой таблицы, а затем столбцов для обновления.Я не уверен, чего вы пытаетесь достичь с помощью оператора выбора, где он находится, но вы можете обновлять результирующий набор из выбранного легально.
Версия sql, если догадаться, что вы имеете в виду, может выглядеть так...

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

ПРИМЕЧАНИЕ: Чтобы использовать оператор select для установки такого значения, вы должны убедиться, что из select будет возвращена только 1 строка!

РЕДАКТИРОВАТЬ :измененное утверждение выше, чтобы отразить то, что я пытался объяснить.Андомар очень хорошо ответил на этот вопрос выше.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top