Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何保证只有一个进程拾取一个处理任务_Sql_Sql Server - Fatal编程技术网

Sql 如何保证只有一个进程拾取一个处理任务

Sql 如何保证只有一个进程拾取一个处理任务,sql,sql-server,Sql,Sql Server,我有多台计算机,它们的任务是发送在公共SQL Server上的表中找到的电子邮件。每台计算机通过查看设置为0的状态标志,轮询电子邮件表以查找它可以发送的邮件。如果计算机执行 SELECT * FROM tblEmailQueue where StatusFlag=0 如果它返回一条记录,它会立即将StatusFlag设置为1,这将导致轮询同一表的另一台计算机找不到该记录。我担心的是,如果两台计算机在更新状态标志之前同时找到记录,电子邮件将发送两次。有没有人对如何确保只有一台计算机获得记录有想法

我有多台计算机,它们的任务是发送在公共SQL Server上的表中找到的电子邮件。每台计算机通过查看设置为0的状态标志,轮询电子邮件表以查找它可以发送的邮件。如果计算机执行

SELECT * FROM tblEmailQueue where StatusFlag=0

如果它返回一条记录,它会立即将
StatusFlag
设置为
1
,这将导致轮询同一表的另一台计算机找不到该记录。我担心的是,如果两台计算机在更新
状态标志之前同时找到记录,电子邮件将发送两次。有没有人对如何确保只有一台计算机获得记录有想法?我知道我可能可以做一个表锁,但我宁愿现在必须这样做

在印第安纳波利斯,我们很熟悉比赛情况;-)

让我们假设您实际拥有和ID字段和StatusFlag字段,并创建一个存储过程,其中包括

declare @id int

select top 1 @id = id from tblEmailQuaue where StatusFlag=0
if @@rowcount = 1
begin

    update tblEmailQuaue set StatusFlag=1 where ID = @id AND StatusFlag=0
    if @@rowcount = 1
    begin
      -- I won the race, continue processing
      ...
    end
end
增加


如果您想要的只是选择的结果,那么像这样的显式处理不如Joachim的方法。但是这种方法这种方法也适用于旧版本的SQL server以及其他数据库。

在印第安纳波利斯,我们熟悉比赛条件;-)

让我们假设您实际拥有和ID字段和StatusFlag字段,并创建一个存储过程,其中包括

declare @id int

select top 1 @id = id from tblEmailQuaue where StatusFlag=0
if @@rowcount = 1
begin

    update tblEmailQuaue set StatusFlag=1 where ID = @id AND StatusFlag=0
    if @@rowcount = 1
    begin
      -- I won the race, continue processing
      ...
    end
end
增加


如果您想要的只是选择的结果,那么像这样的显式处理不如Joachim的方法。但是此方法此方法也适用于旧版本的SQL server以及其他数据库。

您可以使用更新值并立即输出更新的行,而不是使用两个可能导致竞争条件的查询

这将更新statusflag=0的行,并输出所有更新的行

UPDATE tblEmailQueue 
SET statusflag=1 
OUTPUT DELETED.*
WHERE statusflag=0;

编辑:如果您选择一行,您可能需要一些订单。由于更新本身无法排序,因此可以使用公共表表达式进行更新

WITH cte AS (
  SELECT TOP 1 id, statusflag FROM tblEmailQueue 
  WHERE statusflag = 0 ORDER BY id
)
UPDATE cte SET statusflag=1 OUTPUT DELETED.*;

这将更新statusflag=0的行,并输出所有更新的行

UPDATE tblEmailQueue 
SET statusflag=1 
OUTPUT DELETED.*
WHERE statusflag=0;

编辑:如果您选择一行,您可能需要一些订单。由于更新本身无法排序,因此可以使用公共表表达式进行更新

WITH cte AS (
  SELECT TOP 1 id, statusflag FROM tblEmailQueue 
  WHERE statusflag = 0 ORDER BY id
)
UPDATE cte SET statusflag=1 OUTPUT DELETED.*;

.

将另一列添加到表tblEmailQueue(比如UserID),然后尝试提取一封电子邮件,如

--Let flag an email and assign it to the application who made the request
--@CurrentUserID is an id unique to each application or user and supplied by the application
--or user who made the request, this will also ensures that the record is going to 
--the right application and perhaps you can use it for other purpose such as monitoring.

UPDATE tblEmailQueue set UserID = @CurrentUserID, StatusFlag=1 where ID = isnull(
     select top 1 ID from tblEmailQueue where StatusFlag=0 order by ID
), 0)

--Lets get an email that had a flag for the current user id
SELECT * FROM tblEmailQueue where StatusFlag=1 and UserID = @CurrentUserID

向表tblEmailQueue中添加另一列(比如UserID),然后尝试提取一封电子邮件,如

--Let flag an email and assign it to the application who made the request
--@CurrentUserID is an id unique to each application or user and supplied by the application
--or user who made the request, this will also ensures that the record is going to 
--the right application and perhaps you can use it for other purpose such as monitoring.

UPDATE tblEmailQueue set UserID = @CurrentUserID, StatusFlag=1 where ID = isnull(
     select top 1 ID from tblEmailQueue where StatusFlag=0 order by ID
), 0)

--Lets get an email that had a flag for the current user id
SELECT * FROM tblEmailQueue where StatusFlag=1 and UserID = @CurrentUserID

您可以在同一事务中执行选择和发送电子邮件。您还可以使用
ROWLOCK
提示,在发送电子邮件或为
StatusFlag
设置新值之前,不要提交事务。这意味着,只要您提交事务,就没有人(使用提示
NOLOCK
readuncommitted
隔离级别执行事务)可以读取此行

SELECT * FROM tblEmailQueue WITH(ROWLOCK) where StatusFlag=0
此外,您应该检查隔离级别。对于您的案例,隔离级别应为
readcommitted
REPEATABLE READ

请参阅有关隔离级别的信息

您可以在同一事务中执行选择和发送电子邮件。您还可以使用
ROWLOCK
提示,在发送电子邮件或为
StatusFlag
设置新值之前,不要提交事务。这意味着,只要您提交事务,就没有人(使用提示
NOLOCK
readuncommitted
隔离级别执行事务)可以读取此行

SELECT * FROM tblEmailQueue WITH(ROWLOCK) where StatusFlag=0
此外,您应该检查隔离级别。对于您的案例,隔离级别应为
readcommitted
REPEATABLE READ

请参阅有关隔离级别的信息

最佳解决方案。有趣的是,所有其他人都给出了代码,却从未学习过完整的语法,所以他们不知道output关键字;)哇,我不能说以前见过这种构造。让我想知道我还缺少什么:0)谢谢!此语句正在更新使statusflag=1的所有记录。有没有一种方法可以只做一条记录?@Chris Yep,行得通,如果你想对处理过程进行排序,可以在答案中添加另一个选项。@Joachim,你一定读过我的心思,因为我需要添加优先级!我用了你的例子,效果很好!非常感谢。最佳解决方案。有趣的是,所有其他人都给出了代码,却从未学习过完整的语法,所以他们不知道output关键字;)哇,我不能说以前见过这种构造。让我想知道我还缺少什么:0)谢谢!此语句正在更新使statusflag=1的所有记录。有没有一种方法可以只做一条记录?@Chris Yep,行得通,如果你想对处理过程进行排序,可以在答案中添加另一个选项。@Joachim,你一定读过我的心思,因为我需要添加优先级!我用了你的例子,效果很好!非常感谢。