Skip to Main Content
  • Questions
  • Update table rows in prefered row order using sequence

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Robert.

Asked: September 18, 2015 - 1:28 pm UTC

Last updated: October 28, 2021 - 4:14 am UTC

Version: 11.2.0.3

Viewed 50K+ times! This question is

You Asked

Hi Tom,

is it possible to update table rows in prefered row order by using sequence value?
Example:
CREATE TABLE T (ID NUMBER PRIMARY KEY, ORDER_COL NUMBER);
CREATE SEQUENCE SQ_T;

INSERT INTO T(ID, ORDER_COL) VALUES(2,2);
INSERT INTO T(ID, ORDER_COL) VALUES(4,4);
INSERT INTO T(ID, ORDER_COL) VALUES(3,3);
INSERT INTO T(ID, ORDER_COL) VALUES(1,1);
INSERT INTO T(ID, ORDER_COL) VALUES(5,5);

And now i want to update column ORDER_COL BY USING sequence SQ_T, but i want to process UPDATE statement in ordering rows by ID.
So this UPDATE is not correct - SQ_T is not using "in order by ID"
UPDATE T SET ORDER_COL = SQ_T.NEXTVAL;
Result is:
ID ORDER_COL
---------- ----------
1 4
2 1
3 3
4 2
5 5

Is there any possibility how to do it? In UPDATE statement it is not possible to use ORDER BY clause. I can use MERGE:
MERGE INTO T
USING (SELECT ID FROM T ORDER BY ID) ST ON (ST.ID = T.ID)
WHEN MATCHED THEN UPDATE SET T.ORDER_COL = SQ_T.NEXTVAL;

But this is not as expected too:
ID ORDER_COL
---------- ----------
1 4
2 1
3 3
4 2
5 5

I want this result:
ID ORDER_COL
---------- ----------
1 1
2 2
3 3
4 4
5 5

Thanks,
Robert



and Chris said...

Is there any particular reason you want to use a sequence to do this?

You can't refer to nextval or currval in DML subquerys or select statements with order bys:

http://docs.oracle.com/database/121/SQLRF/pseudocolumns002.htm#SQLRF50944

This makes what you request challenging to do in a single statements.

An easier approach is to use row_number(). You can assign the rows increasing values based on your ordering. You can then assign order_col to the values this returns:

CHRIS>update t set order_col = null;

5 rows updated.

Elapsed: 00:00:00.11
CHRIS>
CHRIS>select * from t
  2  order  by 1;

        ID  ORDER_COL
---------- ----------
         1
         2
         3
         4
         5

Elapsed: 00:00:00.23
CHRIS>
CHRIS>update t
  2  set    order_col = (
  3    with rws as (
  4      select row_number() over (order by id) r , id from t
  5    )
  6      select r from rws where t.id = rws.id
  7    );

5 rows updated.

Elapsed: 00:00:00.11
CHRIS>
CHRIS>select * from t
  2  order  by 1;

        ID  ORDER_COL
---------- ----------
         1          1
         2          2
         3          3
         4          4
         5          5


Thanks for providing a complete test case, it does make it much easier to help you!

Rating

  (3 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

A reader, September 18, 2015 - 7:18 pm UTC

Thanks for your answer, Chris, but this is not solution for my problem.
For some reasons i must use sequence.

Robert
Connor McDonald
September 18, 2015 - 11:57 pm UTC

A little plsql should suffice

SQL> declare
  2    type ridlist is table of rowid index by pls_integer;
  3    l_rowid ridlist;
  4  begin
  5    select rowid
  6    bulk collect into l_rowid
  7    from T
  8    order by id;
  9
 10    forall i in 1 .. l_rowid.count
 11      update t set order_col = sq_t.nextval
 12      where rowid = l_rowid(i);
 13  end;
 14  /

PL/SQL procedure successfully completed.

SQL>
SQL> select * from t;

        ID  ORDER_COL
---------- ----------
         2          2
         4          4
         3          3
         1          1
         5          5


Worke Like a charm

A reader, March 04, 2020 - 6:20 pm UTC

I had to change my table's key structure and use a Sequence Number as my primary key since we were redesigning it based on a sequential batch number. Using your solution worked perfectly to initialize the batch number keys the way that I needed to and provided me another way of looking at potential options for using WITH statements in an update.

Thank you.

CHRIS>update t
2 set order_col = (
3 with rws as (
4 select row_number() over (order by id) r , id from t
5 )
6 select r from rws where t.id = rws.id
7 );

Tom
Connor McDonald
March 05, 2020 - 1:10 am UTC

glad we could help

A more simple version

Mony, October 27, 2021 - 2:27 pm UTC

What about this?:

UPDATE (select * from t order by id) SET ORDER_COL = SQ_T.NEXTVAL;

Tested and works.
Connor McDonald
October 28, 2021 - 4:14 am UTC

very nice indeed!

I'll ask around internally to see if we can *guarantee* that this approach will work.

Addenda - parallel DML definitely breaks this approach (as you'd expect) because we have multiple sessions all grabbing sequence values

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.