2
update mytable set node_index=0 where id in (
        SELECT 
            id
         FROM mytable
         WHERE 
            rownum<=10 and PROCS_DT is null  
         order by CRET_DT,PRTY desc) 

I got this query as part of a previous answer to my question. Now how to lock the rows in the select query to make sure no other thread over writes my update.

0

3 Answers 3

5

It's not clear to me that you have the problem you seem to think you have.

Updating a row in the table inherently places a row-level lock on that row. No other session can update that row until you release the lock by ending your transaction by committing or rolling back. Once you complete your transaction, other sessions are free to overwrite your update. There is no need or benefit to locking the rows in the SELECT.

If you are trying to write your application to avoid lost updates (i.e. you want to avoid another session updating the same rows that you did after you committed your transaction without showing the other user your updated data first), your application would need to implement some sort of additional locking. The most efficient would be to implement optimistic locking using some sort of LAST_UPDATE_TIMESTAMP column in the table. Assuming you added this column to the table if it doesn't already have one, you would select the LAST_UPDATE_TIMESTAMP whenever you queried data to present to the user and you would specify that LAST_UPDATE_TIMESTAMP in your UPDATE. If your UPDATE updated exactly 1 row, you know that no one had changed the data since you queried it. If your UPDATE updated 0 rows, you would know that the data had changed since you had queried it and your application would need to take appropriate action (i.e. re-querying the data, re-presenting it to the user, asking the user whether to keep any of the uncommitted changes they had made, etc.).

Your query does have another problem, however. The SELECT statement is almost certainly not doing what you think (or intend) for it to do. This query gets an arbitrary 10 rows from MYTABLE where PROCS_DT is NULL and then orders those arbitrary 10 rows.

         SELECT 
            id
         FROM mytable
         WHERE 
            rownum<=10 and PROCS_DT is null  
         order by CRET_DT,PRTY desc

Assuming that you actually want to get the "top 10" results, you would need to do the ORDER BY in a subquery before applying the ROWNUM predicate, i.e.

         SELECT 
            id
         FROM (SELECT *
                 FROM mytable
                WHERE procs_dt IS NULL
                ORDER BY cret_dt, prty desc)
         WHERE 
            rownum<=10 

Or you could use an analytic function, i.e.

         SELECT 
            id
         FROM (SELECT m.*,
                      rank() over (ORDER BY cret_dt, prty desc) rnk
                 FROM mytable m
                WHERE procs_dt IS NULL)
         WHERE 
            rnk<=10 
Sign up to request clarification or add additional context in comments.

6 Comments

"The most efficient would be to implement optimistic locking using the ORA_ROWSCN pseudocolumn." There seems to be a problem with using ORA_ROWSCN for optimistic locking. See asktom.oracle.com/pls/apex/…
+1 for pointing out that the query in OP's question is broken.
True. Thank you for poiting the problem in my query. As part of previous answer to my stack overflow q i actually got the exact query that you have given me. But it s typo error from my end.
@Shannon - Thanks for that! I had seen that technique in the first edition of Expert Oracle Data Architecture and I've actually used it a time or two without (seemingly) any problems. Hadn't seen that particular test case though. Wish I could +100!
@Shannon - I updated my answer to use a LAST_UPDATED_TIMESTAMP rather than ORA_ROWSCN for the optimistic locking section.
|
3

To lock rows in Oracle, you can do:

SELECT *
  FROM table1
WHERE some_condition
FOR UPDATE OF table1;

It's much better to just lock the rows you need instead of the whole table.

Refer here: http://www.techonthenet.com/oracle/cursors/for_update.php

But in general, if you are doing the UPDATE with a single statement, you should not need to worry about locking the table or even the rows. It's when you have to use multiple statements that you need to lock rows.

A scenario where you would need to use this feature would be in a reservation system. Consider this example:

1. Execute SELECT to find out if room XYZ is available for a reservation on date X.
2. The room is available. Execute UPDATE query to book the room.

Do you see the potential problem here? If between steps 1 and 2 the room gets booked by another transaction, then when we reach step 2 we are operating on an assumption which is no longer valid, namely, that the room is available.

However, if in step 1 we use the SELECT FOR UPDATE statement instead, we ensure that no other transaction can lock that row, so when we go to UPDATE the row, we know it's safe to do so.

3 Comments

thank you. I think my case is also similar to ur hotel reservation example. I have 2 nodes which has node_index values of 0 and 1 which can execute the same update query at the same time. in that case i want different set of rows to be processed by those nodes. If i add select for update in my inner query it is not working. Do you mean my update statement inherently takes care of this scenario
@Shiv - I don't think you need to use the SELECT FOR UPDATE here since you are doing the UPDATE in a single operation. The reservation example is where you query some rows, and then some amount of time passes, and then you update those same rows. Without the SELECT FOR UPDATE, those rows can change. In your case though, you are using a single UPDATE statement, so your UPDATE statement will see the rows as they were when your statement began executing unless of course you are using an unusual isolation level. So in summary, I think a single UPDATE statement should work.
I raised another question in the forum. Please could you help me fix that stackoverflow.com/questions/6770317/…
0

You can try using SKIP LOCKED inside the nested query.

For detail please check this blog:

https://www.2ndquadrant.com/en/blog/what-is-select-skip-locked-for-in-postgresql-9-5/

Not the exact one, but you can take idea from this one:

update mytable set node_index=0 where id in (
        SELECT 
         id
        FROM mytable
        WHERE 
            rownum<=10 and PROCS_DT is null  
        order by CRET_DT,PRTY desc
        FOR UPDATE SKIP LOCKED)
 RETURNING *;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.