基于子查询的更新失败

时间:2009-12-09 12:22:22

标签: sql oracle analytics

我正在尝试在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是外键。我正在尝试根据每次航行中的日期分配一个序列号。

但是,上述情况因 ORA-01732而失败:数据操作操作在此视图中不合法

问题是什么?如何避免?

4 个答案:

答案 0 :(得分:5)

由于您无法使用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
)

它有效,但比我对这项任务的期望更复杂。

答案 1 :(得分:2)

您可以更新某些视图,但有一些限制,一个是视图不能包含分析函数。请参阅SQL Language Reference on UPDATE并搜索“analytic”的首次出现。

如果在同一天没有航行访问多个端口(或者日期包含使其唯一的时间组件),这将有效:

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

答案 2 :(得分:-1)

不要认为你可以更新派生表,我会改写为:

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

答案 3 :(得分:-2)

UPDATE之后的第一个标记应该是要更新的表的名称,然后是要更新的列。我不确定你想用select语句实现什么,但你可以'从合法选择中更新结果集。
一个版本的sql,猜测你的想法,可能看起来像......

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

注意:要使用select语句来设置这样的值,您必须确保从select中只返回1行!

编辑:上面的修改声明反映了我试图解释的内容。 Andomar上面已经很好地回答了这个问题

相关问题