English 中文(简体)
Update based on subquery fails
原标题:

I am trying to do the following update in 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 is the primary key, voyage_id is a foreign key. I m trying to assign a sequence number based on the dates within each voyage.

However, the above fails with ORA-01732: data manipulation operation not legal on this view

What is the problem and how can I avoid it ?

最佳回答

Since you can t update subqueries with row_number, you ll have to calculate the row number in the set part of the update. At first I tried this:

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
)

But that doesn t work, because the subquery only selects one row, and then the row_number() is always 1. Using another subquery allows a meaningful result:

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
)

It works, but more complex than I d expect for this task.

问题回答

You can update some views, but there are restrictions and one is that the view must not contain analytic functions. See SQL Language Reference on UPDATE and search for first occurence of "analytic".

This will work, provided no voyage visits more than one port on the same day (or the dates include a time component that makes them unique):

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
)

I think this handles the case where a voyage visits more than 1 port per day and there is no time component (though the sequence of ports visited on the same day is then arbitrary):

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

Don t think you can update a derived table, I d rewrite as:

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

The first token after the UPDATE should be the name of the table to update, then your columns-to-update. I m not sure what you are trying to achieve with the select statement where it is, but you can update the result set from the select legally.
A version of the sql, guessing what you have in mind, might look like...

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

NOTE: to use a select statement to set a value like this you must make sure only 1 row will be returned from the select !

EDIT : modified statement above to reflect what I was trying to explain. The question has been answered very nicely by Andomar above





相关问题
Export tables from SQL Server to be imported to Oracle 10g

I m trying to export some tables from SQL Server 2005 and then create those tables and populate them in Oracle. I have about 10 tables, varying from 4 columns up to 25. I m not using any constraints/...

Connecting to Oracle 10g with ODBC from Excel VBA

The following code works. the connection opens fine but recordset.recordCount always returns -1 when there is data in the table. ANd If I try to call any methods/properties on recordset it crashes ...

How to make a one to one left outer join?

I was wondering, is there a way to make a kind of one to one left outer join: I need a join that matches say table A with table B, for each record on table A it must search for its pair on table B, ...

Insert if not exists Oracle

I need to be able to run an Oracle query which goes to insert a number of rows, but it also checks to see if a primary key exists and if it does, then it skips that insert. Something like: INSERT ALL ...

How can I store NULLs in NOT NULL field?

I just came across NULL values in NOT-NULL fields in our test database. How could they get there? I know that NOT-NULL constraints can be altered with NOVALIDATE clause, but that would change table s ...

Type reference scope

I m studying databases and am currently working on a object-relational DB project and I ve encountered a small problem with the number of possible constraints in an object table. I m using "Database ...

OracleParameter and DBNull.Value

we have a table in an Oracle Database which contains a column with the type Char(3 Byte). Now we use a parameterized sql to select some rows with a DBNull.Value and it doesn t work: OracleCommand ...

热门标签