我有一个表有点像队列的表,除了从表中删除记录。我试图尝试的是让存储过程返回一个当前没有从前端应用程序处理的记录。我们所拥有的是一个“锁定”列,我们将其设置为表明这一点。我们这样做的原因是,一次只有一个呼叫中心代理可以处理记录。这是我的sql到目前为止的样子。问题是如果我从两个单独的会话运行此查询(第二个会话注释掉waitfor语句),第二个会话不会返回任何记录10秒。我在选择记录时将其缩小到order by子句。如果我删除Order By它返回但我需要按顺序。
或许我的查询完全错了?我应该使用事务隔离级别(可序列化,快照)??任何指导都会很棒!
DECLARE @WorkItemId INT;
BEGIN TRANSACTION
/* TODO: Skip Records That Have Been Completed. */
SET @WorkItemId = (SELECT TOP 1 CampaignSetDetailId FROM CampaignSetDetail WITH (XLOCK, READPAST) WHERE LockedBy IS NULL ORDER BY NEWID(), NumAttempts ASC);
/* */
UPDATE CampaignSetDetail SET LockedBy = 'MPAUL', LockedDTM = GETUTCDATE() WHERE CampaignSetDetailId = @WorkItemId;
/* */
SELECT * FROM CampaignSetDetail WHERE CampaignSetDetailId = @WorkItemId;
WAITFOR DELAY '00:00:10';
COMMIT TRANSACTION
答案 0 :(得分:0)
尝试以下操作 - 一组重试尝试,并使用更新锁定。
DECLARE @RetryCount Int = 1
DECLARE @MaxRetries Int = 5;
SET @RetryCount = 1
WHILE @RetryCount < @MaxRetries
BEGIN
BEGIN TRY
/* TODO: Skip Records That Have Been Completed. */
SET @WorkItemId = (
SELECT TOP 1
CampaignSetDetailId
FROM CampaignSetDetail WITH (UPDLOCK)
WHERE LockedBy IS NULL
ORDER BY NEWID()
,NumAttempts ASC
);
UPDATE CampaignSetDetail WITH (UPDLOCK)
SET LockedBy = 'MPAUL'
,LockedDTM = GETUTCDATE()
WHERE CampaignSetDetailId = @WorkItemId;
SELECT *
FROM CampaignSetDetail WITH (UPDLOCK)
WHERE CampaignSetDetailId = @WorkItemId;
SELECT @RetryCount = @MaxRetries;
END TRY
BEGIN CATCH
IF ERROR_NUMBER() IN (1204, 1205, 1222)
BEGIN
SET @RetryCount += 1;
WAITFOR DELAY '00:00:02';
END
ELSE
THROW;
END CATCH
END
有关特定锁定错误的详细信息,请参阅here。即使从其他会话执行同一表的更新,这也应该有效。
答案 1 :(得分:0)
你可以在一个声明中做到这一点
DECLARE @WorkItemId INT;
UPDATE [CampaignSetDetail]
SET
[LockedBy] = 'MPAUL',
[LockedDTM] = GETUTCDATE()
WHERE
[CampaignSetDetailId] = (
SELECT TOP 1
[CampaignSetDetailId],
@WorkItemId = [CampaignSetDetailId]
FROM
[CampaignSetDetail]
WHERE
[LockedBy] IS NULL
ORDER BY
[NumAttempts]);
SELECT
* -- You shouldn't do this.
FROM
[CampaignSetDetail]
WHERE
[CampaignSetDetailId] = @WorkItemId;
我假设[CampaignSetDetailId]
在表格上形成聚集索引。您可以考虑像
CREATE INDEX [IX_CampaignSetDetail_NumAttempts]
ON [CampaignSetDetail]([NumAttempts])
WHERE [LockedBy] IS NULL;
优化此操作。
答案 2 :(得分:0)
我认为我找到了一个很好的解决方案,但我仍在评估所有答案。我也在尝试使用CTE,这给了我最好的表现。
DECLARE @WorkItemId INT;
WITH CTE AS (
SELECT TOP 1 [CampaignSetDetailId], LockedBy FROM [dbo].[CampaignSetDetail] WITH (ROWLOCK, READPAST) WHERE LockedBy IS NULL ORDER BY NEWID(), NumAttempts ASC
)
UPDATE CTE SET LockedBy = 'DIESEL', @WorkItemId = [CampaignSetDetailId]
SELECT @WorkItemId
WAITFOR DELAY '00:00:05';