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
perid
using a single SQLUPDATE
?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
andindex
.
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.