Обновление на основе подзапроса не выполнено
Вопрос
Я пытаюсь сделать следующее обновление в 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 строка!
РЕДАКТИРОВАТЬ :измененное утверждение выше, чтобы отразить то, что я пытался объяснить.Андомар очень хорошо ответил на этот вопрос выше.