从EXEC stored_procedure捕获OUTPUT deleted.x的正确方法是什么?

时间:2017-01-14 16:52:01

标签: sql-server tsql stored-procedures

我在高并发条件下使用表作为FIFO queue,并像这样原子地出列:

CREATE PROCEDURE [jobs].[DequeueJob]        
AS
BEGIN
    DELETE TOP(1) 
    FROM jobs.JobQueue WITH (ROWLOCK, READPAST) 
    OUTPUT deleted.JobID;
END

我从另一个调用此存储过程来捕获并使用deleted.JobID,所以我有这个:

CREATE PROCEDURE [jobs].[GetJob]        
AS
BEGIN
    DECLARE @Job TABLE (JobID int)       

    INSERT @Job EXEC jobs.DequeueJob    

    DECLARE @JobID int
    SET @JobID = (SELECT JobID from @Job)   

    UPDATE jobs.Jobs 
    SET IsInQueue = 0 
    WHERE JobID = @JobID

    SELECT * 
    FROM jobs.Jobs 
    WHERE JobID = @JobID
END

这很好用,但它闻起来:要获得我想要使用的值(JobID),我必须创建一个包含一列的表变量,INSERT一行,并且然后立即SELECT同一行退出。当我调用deleted.JobID而不会破坏其原子性时,是否有更好的方法(从可读性或优化的角度来看)捕获EXEC jobs.DequeueJob

EXEC @JobID = jobs.DequeueJob是有效的语法,看起来很有前途,但它捕获了返回值,而不是OUTPUT值。 This answer对几个结果传递选项进行了很好的讨论,但我找不到适合我的情况的方法。

我正在使用SQL Server 2012 SP3,但如果在较新版本中提供了改进的语法,我准备升级。

1 个答案:

答案 0 :(得分:1)

我同意Shakeer Mirza的观点,即将OUTPUT子句结果插入表中是不可避免的,因为这是在T-SQL中使用该值的唯一方法。除非,你有一个特定的理由进行单独的触发,你可以像下面的例子一样进行重构,这个例子稍微不那么臭了,恕我直言。

CREATE PROCEDURE [jobs].[GetNextJob]        
AS

DECLARE @Job TABLE ( JobID int PRIMARY KEY );

DELETE TOP(1) FROM jobs.JobQueue WITH (ROWLOCK, READPAST)
OUTPUT deleted.JobID INTO @Job(JobID);

UPDATE jobs.Jobs set IsInQueue = 0 
OUTPUT inserted.*
WHERE JobID = (SELECT JobID from @Job);
GO

请注意,DELETE TOP没有定义的顺序,因此,如果您需要FIFO,请考虑在ORDER BY中使用带有DELETE的子查询。