1

我正在尝试在 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

4 回答 4

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
)

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

于 2009-12-09T13:01:34.017 回答
2

您可以更新某些视图,但有一些限制,其中之一是视图不得包含分析函数。请参阅有关 UPDATE 的 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
     )
)
于 2009-12-09T12:30:48.517 回答
-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
于 2009-12-09T12:26:44.867 回答
-2

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

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

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

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

于 2009-12-09T12:30:47.340 回答