Sql EXEC语句和事务范围

Sql EXEC语句和事务范围,sql,sql-server-2008,transactions,Sql,Sql Server 2008,Transactions,起初这似乎很容易,但我知道的一切似乎又错了 查看PAQ的共识似乎是EXEC不会启动隐式事务,您可以通过以下方式进行测试: create procedure usp_foo as begin select @@trancount; end go exec usp_foo; 返回0 但是,如果您使用T-SQL调试器执行此操作@根据监视,事务实际上是过程中的1,尽管它返回0 因此,我认为这是调试器的一个副作用,但随后我编写了一些代码来测试它,在表中执行更新,然后从中选择max(id),经典:

起初这似乎很容易,但我知道的一切似乎又错了

查看PAQ的共识似乎是EXEC不会启动隐式事务,您可以通过以下方式进行测试:

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

exec usp_foo;
返回0

但是,如果您使用T-SQL调试器执行此操作@根据监视,事务实际上是过程中的1,尽管它返回0

因此,我认为这是调试器的一个副作用,但随后我编写了一些代码来测试它,在表中执行更新,然后从中选择max(id),经典:

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
因此,我希望这会给出重复的id,因为并行执行的2次更新可以在2次选择获取最后一个id并返回相同的值之前完成,但尽管我可能从多台机器上点击它,但我无法使其中断。当监控机器上的锁和事务时,它会报告exec正在事务中发生,重要的是,exec中的所有语句都被视为一个工作单元/一个事务

我可以理解更新是否在事务中,这是锁定的原因,但锁定似乎会一直保留到选择之后

如果我使用探查器跟踪,我可以看到为整个EXEC语句的执行提供了一个事务ID,并且事务ID并不像我在执行时预期的那样为0


有人能向我解释一下我错过了绘图的地方吗?或者我错了吗?事实上,这样生成ID是安全的?

您的测试必须给出正确的结果,因为您的速度不够快,无法调用这两个语句之间的第二个调用。尝试添加延迟,您可以看到测试将开始失败

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
在另一个会话中,尽快发出一个
EXEC-GetNextID
并发出另一个
EXEC-GetNextID
。大约5秒钟后,两个EXEC将返回相同的结果,即不正确的值。现在将SP更改为

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

并重复上述测试。您将看到两个调用都将返回正确的值。此外,第二次调用(如果尽快发出)将在大约10秒内返回结果,因为更新被阻止,必须等待5秒(第一次调用提交)然后等待5秒。

您的测试必须给出正确的结果,因为您的速度不够快,无法在这两条语句之间调用第二个调用。尝试添加延迟,您可以看到测试将开始失败

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
在另一个会话中,尽快发出一个
EXEC-GetNextID
并发出另一个
EXEC-GetNextID
。大约5秒钟后,两个EXEC将返回相同的结果,即不正确的值。现在将SP更改为

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

并重复上述测试。您将看到两个调用都将返回正确的值。此外,第二次调用(如果尽快发出)将在大约10秒钟内返回结果,因为更新被阻止,必须等待5秒钟(第一次调用提交),然后等待5秒钟。

Ahhh,我已经在这方面找到了答案。我获取调用的输出并将其插入到哈希表中。因为整个语句(包括exec)构成了insert的一个隐式事务的一部分,所以它使用环境事务正确地保护了调用。显然,在没有内联插入的情况下调用此函数将产生重复的结果。谢谢啊,借助这个,我已经弄明白了。我获取调用的输出并将其插入到哈希表中。因为整个语句(包括exec)构成了insert的一个隐式事务的一部分,所以它使用环境事务正确地保护了调用。显然,在没有内联插入的情况下调用此函数将产生重复的结果。谢谢