尝试在一个事务中查询然后更新表

时间:2013-01-06 23:35:59

标签: sql sql-server sql-server-2008-r2

存储过程的规范是:
要从我的表格Id中选择并返回tb_r12028dxi_SandpitConsoleProofClient(订单并不重要,只有前面的1个会发现),一旦我选择了该记录,就需要将其标记为{{1}这样它就不会再次被选中。

这是存储过程:

'P'

它返回错误消息:

  

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

我刚刚添加了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 脚本以及UpdateBEGIN 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;因为我想确保数据被读入输出变量并且UPDATE发生了。我应该省略程序的这一部分吗?

3 个答案:

答案 0 :(得分:5)

你有“回归”;在“COMMIT TRANSACTION”之前这意味着“COMMIT TRANSACTION;”永远不会被执行。

答案 1 :(得分: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

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

答案 2 :(得分:2)

作为完全合理的suggestion by @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()函数会为[Status] IS NULL的所有行分配排名,这样您只能更新特定的行。

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