If you have written code for the web that involves select for update, no doubt you have run into problems where the web session somehow disappeared, and the database didn't realize it. The result is that the record remained locked until you killed the session. I've been involved with several clients now where this is a common problem, so common that they have an automated routine to "clean up" these zombie sessions.
So what do you do if you are writing code, and you don't want to use that pesky FOR UPDATE syntax? Oracle Database 10g offers ORA_ROWSCN!! ORA_ROWSCN provides an answer to consistency, without needing to lock a row. ORA_ROWSCN is a new pseudo column available in Oracle Database 10g. It provides the SCN associated with each individual row in a table when it was read. One thing to note, by default Oracle will not track SCN's at the row level in a table, so you need to enable row level dependency tracking by using the ROWDEPENDENCIES keyword in the CREATE TABLE command. We have noticed no additional performance issues with this feature enabled. Still, it's always a good idea to test things before you just start using them in production! :-)
Here is an example. In an HTMLDB application (but this technique can be used in any kind of 10g application), we want to read in a record from the database and offer the potential to edit the record. We don't want to lock it at this point for a number of reasons (for example, our user might just leave the record up on his/her web browser for an hour). So, we use something like this PL/SQL code when we load our page for the first time:
select b.name, a.adr_id, a.address, a.ora_rowscn,
from addresses a, place b
Note in this code that we load in the ORA_ROWSCN values for both the tables/rows we are reading. Now, we make changes to this data, and we get ready to update it. How do we know, though, that someone else has not changed the data?
We could requery the record just before we write it and make sure the data is still the same, but then we really need to use the FOR UPDATE clause again, and we want to avoid that. If we don't use the FOR UPDATE clause, someone could still change the data between the query and the update.
The solution is to use ORA_ROWSCN in your update clause to ensure that the row has not changed since the last time you read it. Here is an example (again, a PL/SQL code block from HTMLDB):
-- Declare the exception
-- Update the HSD-unit_codes table
-- See above where we compare the ROWSCN list value to the
-- current ORA_ROWSCN of the table?
-- Note that SQL%ROWCOUNT=0 if we didn’t update a row.
-- This means that the update failed.
-- We clear the cache of the page.
-- This raises the error to the user that the record was already
raise_application_error(-20000, 'Record has already been changed by another user.');
-- I set this to allow a display message to be printed on
-- the refresh.
Note in the WHERE clause that I check the recorded ORA_ROWSCN against the current value of ORA_ROWSCN for that table. If the record has changed since I read it, then ORA_ROWSCN for the row will be different than the one we stored when we did the earlier select, and then an exception will be raised.
I've found this to be much easier to use that HTMLDB's automated row processing myself. What do you think?
6/11/2005 Note: I edited this blog to fix an entry that was in error. It indicated that the ROWDEPENDENCIES keyword could be used with the alter table command. In a comment, Tom Kyte has pointed out this is not the case, so I've made that correction here.