EXPLAIN EXTENDED

How to create fast database queries

Archive for August 12th, 2009

Oracle: updating rows with partitioned incremental value

Comments enabled. I *really* need your comment

From Stack Overflow:

I have a table with these example data:

id idx data
1 1 A
1 3 B
2 1 C
2 1 D
2 5 E

I want:

id idx data
1 1 A
1 2 B
2 1 C
2 3 D
2 4 E

Is there a way to renumber the index per id using a single SQL UPDATE?

Note that the order of items should be preserved (i. e. the item E should still be after the items C and D but the order of C and D doesn't really matter).

The goal is to be able to create a primary key over id and index.

If not for the id, the query would be very simple:

UPDATE  t_update
SET       index = rownum

However, the query above does not take id into account.

To update the table using partitioned row numbers, we need to employ the window function ROW_NUMBER().

However, it's usable neither in the UPDATE query itself, nor in an inline view, and attempt to use it produces an error:

ORA-30483: window  functions are not allowed here

To work around the, we should rewrite the UPDATE query as MERGE.

MERGE requires two rowsets (source and target) and they need to be joined on something.

We will use the table itself as a source They only field we can join on here is ROWID pseudocolumn.

Here's the query to do this:

MERGE
INTO    t_update u
USING   (
SELECT  rowid AS rid,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY idx) AS rn
FROM    t_update
)
ON      (u.rowid = rid)
WHEN MATCHED THEN
UPDATE
SET     idx = rn

, and here's the result:

SELECT  *
FROM    t_update

ID IDX DATA
1 1 A
1 2 B
2 1 C
2 2 D
2 3 E

Works fine.

Written by Quassnoi

August 12th, 2009 at 11:00 pm

Posted in Oracle