The exact behaviour of concurrent modifications depends, in part, on the access path chosen by SQL Server to locate records to change.
If SQL Server uses the clustered index to locate data to change, locks will generally be taken on clustered index keys (and/or pages, etc.). If SQL Server locates rows to change using a nonclustered index, locks will be taken on the nonclustered index. In both cases, exclusive locks are also generally taken on the clustered index before the modification is actually performed.
Anticipating locking behaviour can be a fun and educational exercise, but it often the wrong question to ask. If you are experiencing blocking on locks where it does not seem necessary, then it might be the right question, but it is a very advanced one, requiring detailed internal knowledge to fully explain. I'll show an example based on your data later on.
Most often, the real question is "which isolation level should I use?". Locks are an implementation detail, used to provide the guarantees offered by the various isolation levels. The guarantees are the important thing. You should understand the different behaviours that are possible under each isolation level, and then make an informed choice. Please refer to that link for all the details.
When modifying data, RCSI
behaves the same as standard READ COMMITTED
. It will block if it needs to read something that is currently locked by another session's uncommitted changes. Once the blocking session commits or rolls back its changes, the blocked update continues, reading the committed values present at the time the blocking lock was released. This behaviour is required to prevent "lost updates", which are not allowed under any isolation level supported by SQL Server.
The following demo shows that the precise blocking behaviour depends on which locks are needed according to the query plan selected by the optimizer. In some cases, the update will block, in other cases, it will not. SQL Server always respects the guarantees provided by the user's isolation level, regardless of the implementation-defined locking behaviour.
Test Table and Data
CREATE TABLE dbo.Table1
(
PKcol integer PRIMARY KEY,
NonPKCol integer NULL UNIQUE,
col1 integer NULL
);
INSERT dbo.Table1
(PKcol, NonPKCol, col1)
VALUES
(1,1,0),
(2,2,0),
(3,3,0),
(4,4,0),
(5,5,0);
Uncommitted Update
On a separate connection, run:
BEGIN TRANSACTION;
UPDATE dbo.Table1 SET NonPKCol = 997 WHERE PKcol = 3;
UPDATE dbo.Table1 SET NonPKCol = 998 WHERE NonPKCol = 3;
UPDATE dbo.Table1 SET NonPKCol = 999 WHERE NonPKCol = 5;
Note the lack of a COMMIT
or ROLLBACK TRANSACTION
.
Test Results
-- (1) Succeeds (no conflicting locks encountered)
update table1 set col1 = col1 + 1 where PKcol < 3
-- (2) Waits for an X lock for clustered index key PKcol = 3
update table1 set col1 = col1 + 1 where PKcol = 3
-- (3) Waits on U lock for clustered index key PKcol = 3
update table1 set col1 = col1 + 1 where NonPKcol < 3
-- (3) Succeeds when read access is by NONCLUSTERED index
update t set col1 = col1 + 1 from table1 t with(index(2)) where NonPKcol < 3
-- (4) Blocks on U lock for NONCLUSTERED index key NonPKcol = 3
update table1 set col1 = col1 + 1 where NonPKcol = 3
-- (5) Blocks on U lock for nonclustered index key NonPKcol = 5
update table1 set col1 = col1 + 1 where PKcol < 3 and NonPKcol = 5
-- (5) Succeeds when access is by CLUSTERED index
update t set col1 = col1 + 1 from table1 t with(index(1)) where PKcol < 3 and NonPKcol = 5
Best Answer
FOR UPDATE
is not valid SQL Server syntax in a regular SQL statment, it is used when you create a cursor.But you are probably using ADO with CursorLocation
adUseServer
and then your query actually works because ADO will use sp_cursoropen which accepts the syntax used for cursors.The default behavior in SQL Server is that cursors can be updated so specifying
for update
does nothing for you unless you also specify a column list.Specifying the
updlock
query hint on a cursor will only do things for you if you are running in a transaction. Withupdlock
the locks is placed when you dofetch next from ...
and withoutupdlock
the lock is placed when you doupdate ... where current of
, still only if you are in a transaction.So in your case, using
updlock
will place locks when you fetch data if you are in a transaction. If you don't useupdlock
, in a transaction, you will place the locks when you update the data. If no transaction is present there is no difference between the two and you could as well not use any of them.