Sql 尝试在一个事务中查询并更新表

Sql 尝试在一个事务中查询并更新表,sql,sql-server,sql-server-2008-r2,Sql,Sql Server,Sql Server 2008 R2,存储过程的规范是: 要从我的表中选择并返回Id(顺序并不重要,只有找到的前1个才重要),并且在我选择该记录后,需要将其标记为'P',以便不再选择它 以下是存储过程: ALTER PROCEDURE [dbo].[r12028dxi_SandpitConsoleProofSweep] @myId INT OUTPUT AS /* DECLARE @X INT EXECUTE [xxx].[dbo].[r12028dxi_SandpitConsoleProofSweep] @X OUTPU

存储过程的规范是:
要从我的表中选择并返回
Id
(顺序并不重要,只有找到的前1个才重要),并且在我选择该记录后,需要将其标记为
'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
它正在返回错误消息:

执行后的事务计数表示不匹配的事务数 开始并提交语句。上一次计数=0,当前计数=1

我刚刚添加了
Update
脚本和
BEGIN事务
提交事务在此之前,当它看起来如下所示时,它工作正常

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
我添加了
开始事务/
提交事务在“提交事务”之前有“返回”;表示“提交事务”;永远不会执行。

在“提交事务”之前有“返回”;表示“提交事务”;永远不会执行。

给出您想要的:

一旦我选择了那个记录,它就需要被标记为“p”,所以 它不会再次被选中

您可以在一次交易中(而不是在一次交易中)实现这一点

注意:在处理并发处理(即:两个线程试图从单个队列中选择)时,锁定行为比在单个事务中执行更重要。

给出您想要的:

一旦我选择了那个记录,它就需要被标记为“p”,所以 它不会再次被选中

您可以在一次交易中(而不是在一次交易中)实现这一点


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

作为完全合理的选择,您还可以使用和函数,如下所示:

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\u NUMBER()
函数为
[Status]为空的所有行分配排名,这允许您仅更新特定的行


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

作为完全合理的替代方案,您还可以使用a和函数,如下所示:

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\u NUMBER()
函数为
[Status]为空的所有行分配排名,这允许您仅更新特定的行



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

+1谢谢-我需要进一步阅读有关正确使用脚本
事务的信息
+1谢谢-我需要进一步阅读有关正确使用脚本
事务的信息
我不能100%确定您想要实现什么,但我认为仍有人可以在您选择和更新之间插入一条新记录-具体取决于您的事务隔离级别。如果需要防止使用重复性的东西read@Greg我试图实现这样一种情况:我从前1条记录中选择
Id
(顺序不重要),并且一旦我选择了该记录,就需要将其标记为
'P'
。我编辑了这篇文章slightly@Greg在操作开始时添加了存储过程规范。我不能100%确定您想要实现什么,但我认为仍有人可以在您选择和更新之间插入一条新记录,具体取决于您的事务隔离级别。如果需要防止使用重复性的东西read@Greg我试图实现这样一种情况:我从前1条记录中选择
Id
(顺序不重要),并且一旦我选择了该记录,就需要将其标记为
'P'
。我编辑了这篇文章slightly@Greg在OP+1开始时添加了存储过程的规范。感谢Andrew-因此处理器将始终
首先选择
(即子查询),然后
更新
下一步?是的,您可能还希望向查询添加锁提示,即:
FROM xxx.DBO.tb_r12028dxi_sandpitConsoleProfclient x WITH(UPDLOCK,ROWLOCK)
防止两个线程更新和检索同一个IDEExcellent。还有一个愚蠢的问题,
RETURN
的意义是什么?我假设当一个proc有一个
输出
参数时可以使用它?
RETURN
是一种显式退出存储的proc的方法,如果您可以轻松检查是否需要进一步的工作,通常会出现在顶部附近,即:
如果不存在(从xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient x中选择“has unprocessed records”,其中ISNULL(sub.[Status],“”)!=“P”)返回
是的,这是正确的,但我建议对计数使用EXIST检查(将减少读取以确定是否需要继续该进程)+1感谢Andrew-因此处理器将始终
首先选择
(即子查询),然后
更新
下一步?是的,您可能还希望在查询中添加锁定提示,即:
FROM xxx.DBO.tb_r12028dxi_SandpitConsoleProofClient x WITH(UPDLOCK,ROWLOCK)
防止两个线程更新和检索同一个IDEExcellent。还有一个愚蠢的问题——
RETURN
的意义是什么——我认为当一个进程有一个
OUTPUT
参数时可以使用它?
RETURN
是一种显式退出存储进程的方法,如果您可以轻松检查,通常会在顶部附近看到是否需要进一步的工作,即:
如果不存在(从xxx.DBO.tbr12中选择“有未处理的记录”