开发者

EXEC statement and Transaction Scope

开发者 https://www.devze.com 2023-03-09 18:20 出处:网络
This seemed so easy at first but everything I know seems to be wrong again. Looking at PAQ\'s the consensus seems to be that EXEC does not start an implicit transaction, you can test this doing :

This seemed so easy at first but everything I know seems to be wrong again.

Looking at PAQ's the consensus seems to be that EXEC does not start an implicit transaction, you can test this doing :

create procedure usp_foo
as
begin
  select @@trancount;
end
go

exec usp_foo;

which returns 0.

If you however step through this with the T-SQL debugger @@Transaction is in fact 1 inside the procedure according to the watch, although it does return 0 ...

So I think this is a side-effect of the debugge开发者_如何转开发r, but then I write some code to test it, do an update in a table and then select max (id) out the, the classic :

create table nextid
(
  id int
)

insert into nextid values (0)

create procedure nextid
as
BEGIN
  UPDATE nextid set id = id + 1
  select max(id) from nextid
END

So I am expecting this to give out duplicate id's as executed in parallel 2 updates can complete before the 2 selects fetching the last id and returning the same value, but try as I might hitting it from multiple machines I cannot make it break. When monitoring the locks and transactions on the machine it reports that the exec is happening in a transaction and importantly, all the statements inside the exec are treated as one unit of work/one transaction.

I would understand if the update was in a transaction and this was the cause of the lock, but the lock seems to remain until after the select.

If I trace with profiler I can see that a transaction ID is provided for the entire execution of the EXEC statement, and transaction ID is not 0 as I would expect while executing ...

Can someone please explain to me where I am missing the plot or am I wrong and it is in fact safe to generate ID's like that?


Your test must be giving you correct results because you are not fast enough to invoke the second call in between these two statements. Try adding a delay and you can see that the test would start failing.

CREATE TABLE NextID
(
    ID int
)
GO

INSERT INTO NextID VALUES (0)
GO

CREATE PROC GetNextID
AS
BEGIN
    UPDATE NextID SET ID = ID + 1
    WAITFOR DELAY '00:00:05'
    SELECT Max(ID) FROM NextID
END

Issue an EXEC GetNextID and issue another EXEC GetNextID as soon as you can from another session. About 5 seconds later both EXEC would return same result i.e. incorrect value. Now change the SP to

CREATE PROC GetNextID
AS
BEGIN
    BEGIN TRAN

    UPDATE NextID SET ID = ID + 1
    WAITFOR DELAY '00:00:05'
    SELECT Max(ID) FROM NextID

    COMMIT TRAN
END

and repeat the above test. You would see that both calls would return correct value. In addition second call (if issued as soon as possible) would return result approximately in 10 seconds because the UPDATE is blocked and has to wait 5 seconds (for the first call to COMMIT) and then its own 5 second wait.

0

精彩评论

暂无评论...
验证码 换一张
取 消