1

存储过程的规范是:从我的表中
选择并返回(顺序并不重要,只要找到的前 1 个就可以了),一旦我选择了该记录,就需要对其进行标记,以免它被选中再次。Idtb_r12028dxi_SandpitConsoleProofClient'P'

这是存储过程:

ALTER PROCEDURE [dbo].[r12028dxi_SandpitConsoleProofSweep]
    @myId INT OUTPUT
AS

/*
DECLARE @X INT
EXECUTE [xxx].[dbo].[r12028dxi_SandpitConsoleProofSweep] @X OUTPUT
SELECT @X
*/

DECLARE @NumQueue INT = (
            SELECT [cnt] = COUNT(*) 
            FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient
            WHERE [Status] IS NULL
            );
IF @NumQueue > 0 
    BEGIN

        BEGIN TRANSACTION;

        DECLARE @foundID INT = (SELECT TOP 1 Id FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient WHERE [Status] IS NULL);

        UPDATE x
        SET x.[Status] = 'P'
        FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient x
        WHERE x.Id = @foundID 

        SET @myId = @foundID;

        RETURN;

        COMMIT TRANSACTION;

    END;
GO

它正在返回错误消息:

EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。先前计数 = 0,当前计数 = 1。

我刚刚添加了Update脚本,BEGIN TRANSACTION;并且COMMIT TRANSACTION;在此之前它看起来像下面这样运行良好......

ALTER PROCEDURE [dbo].[r12028dxi_SandpitConsoleProofSweep]
    @myId INT OUTPUT
AS

/*
DECLARE @X INT
EXECUTE [xxx].[dbo].[r12028dxi_SandpitConsoleProofSweep] @X OUTPUT
SELECT @X
*/

DECLARE @NumQueue INT = (
            SELECT [cnt] = COUNT(*) 
            FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient
            WHERE [Status] IS NULL
            );
IF @NumQueue > 0 
    BEGIN


    SELECT TOP 1 @myId = Id FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient;
    RETURN;


    END;
GO

我添加了BEGIN TRANSACTION;/COMMIT TRANSACTION;因为我想确保数据被读入输出变量并且更新发生。我应该省略程序的这一部分吗?

4

3 回答 3

5

你有“返回;” 在“提交交易”之前;意思是“提交交易”;永远不会被执行。

于 2013-01-06T23:39:12.457 回答
2

作为@Andrew Bickerton 完全合理建议的替代方案,您还可以使用CTEROW_NUMBER()函数,如下所示:

WITH ranked AS (
  SELECT
    Id,
    [Status],
    rnk = ROW_NUMBER() OVER (ORDER BY Id)
  FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient
  WHERE [Status] IS NULL
)
UPDATE ranked
SET
  [Status] = 'P',
  @myId = Id
WHERE rnk = 1
;

ROW_NUMBER()函数将排名分配给 where 的所有行[Status] IS NULL,这允许您仅更新特定的行。

在这种情况下,使用 CTE 作为 UPDATE 语句的直接目标是绝对合法的,因为 CTE 只从一个表中提取行。(这类似于在 UPDATE 语句中使用视图。)

于 2013-01-07T15:06:49.777 回答
2

给你想要的:

一旦我选择了该记录,就需要将其标记为“P”,以免再次被选中。

您可以在单个语句中实现这一点(而不是在交易中)

ALTER PROCEDURE [dbo].[r12028dxi_SandpitConsoleProofSweep]
    @myId INT OUTPUT
AS
BEGIN
    UPDATE x
        SET x.[Status] = 'P',
            @myID = x.ID
        FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient x
        /* a sample join to get your single row in an update statement */
        WHERE x.ID = (SELECT MIN(ID)
                      FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient sub
                      WHERE ISNULL(sub.[Status], '') != 'P')
END

注意:在处理并发处理时(即:两个线程试图从单个队列中进行选择),更多的是关于锁定行为,而不是在单个事务中进行。

于 2013-01-07T09:45:50.850 回答