使用TOP子句在同一事务中选择和删除

时间:2012-02-23 09:10:44

标签: sql-server sql-server-2008 sql-server-2005

我有快速继续添加数据的表格。

我需要从此表中获取记录并立即将其删除,以便我无法再次处理相同的记录。由于数据的添加速度更快,我需要使用TOP clause,因此当时只有少量记录转到业务逻辑进行处理。

我使用以下查询

BEGIN TRAN readrowdata
SELECT    
 top 5 [RawDataId],    
 [RawData]    
FROM    
 [TABLE]  with(HOLDLOCK)  

 WITH  q AS
    (
        SELECT    
        top 5 [RawDataId],    
        [RawData]    
        FROM    
        [TABLE]  with(HOLDLOCK)  
    )

DELETE from q  
COMMIT TRANSACTION readrowdata  

我在这里使用HOLDLOCK,因此当我执行SELECT和DELETE操作时,新数据无法插入到表中。我使用它是因为假设现在表中只有3条记录,那么SELECT语句将获得3条记录,同时插入新记录,DELETE语句将删除4条记录。所以我会在这里丢失1个数据。

性能期限的查询是否正常?如果我可以改进它,那么请提供你的建议。

谢谢

5 个答案:

答案 0 :(得分:3)

就个人而言,我会采用不同的方法。锁定较少的一个,但也有额外的信息表明当前正在处理某些记录......

DECLARE @rowsBeingProcessed TABLE (
    id INT
);

WITH rows AS (
  SELECT top 5 [RawDataId] FROM yourTable WHERE processing_start IS NULL
)
UPDATE rows SET processing_start = getDate() WHERE processing_start IS NULL
OUTPUT INSERTED.RowDataID INTO @rowsBeingProcessed;

-- Business Logic Here

DELETE yourTable WHERE RowDataID IN (SELECT id FROM @rowsBeingProcessed);

然后,您还可以添加“如果记录已被'处理'超过10分钟,假设业务逻辑失败”等检查,等等。

答案 1 :(得分:1)

通过以这种方式锁定表,您强制其他进程等待您的事务完成。这可能会对可伸缩性和性能产生非常快速的影响 - 而且往往难以预测,因为通常有一系列组件都依赖于您的数据库。

如果每个运行此查询的客户端多个,并且多个客户端向表中添加新行,则整个系统性能可能会在某些时候恶化,因为每个“读取”客户端都在等待锁定,等待插入数据的“写”客户端增长,反过来它们可能会占用其他组件(无论是生成要插入的数据)。

迭戈的答案就是钱 - 将数据放入变量,并删除匹配的行。如果可以避免,请不要在SQL Server中使用锁!

答案 2 :(得分:0)

如果我理解正确,你担心在你的选择和你的删除之间会插入更多的记录而第一个TOP 5会与第二个TOP 5不同吗?

如果是这样,你为什么不把你的第一个选择加载到临时表或变量(或者至少是PK)中,你做了什么与你的数据有关,然后根据这个表进行删除?

答案 3 :(得分:0)

您可以使用TRIGGERS轻松完成。下面提到的是一种情况,它可以帮助您不要让其他试图同时插入数据的用户。如下......

数据定义语言

CREATE TABLE SampleTable
(
    id int
)

示例记录

insert into SampleTable(id)Values(1)

示例触发

CREATE TRIGGER SampleTableTrigger
on SampleTable AFTER INSERT
AS
IF Exists(SELECT id FROM INSERTED)
BEGIN
    Set NOCOUNT ON
    SET XACT_ABORT ON
    Begin Try
        Begin Tran
                 Select ID From Inserted
                 DELETE From yourTable WHERE ID IN (SELECT id FROM Inserted);

        Commit Tran
    End Try
    Begin Catch
        Rollback Tran
    End Catch


End

希望这是非常简单和有用的

答案 4 :(得分:0)

我知道这是一个古老的问题,但我在这里找到了一些解决方案https://www.simple-talk.com/sql/learn-sql-server/the-delete-statement-in-sql-server/

DECLARE @Output table
(
  StaffID INT,
  FirstName NVARCHAR(50),
  LastName NVARCHAR(50),
  CountryRegion NVARCHAR(50)
);
DELETE SalesStaff
OUTPUT DELETED.* INTO @Output
FROM Sales.vSalesPerson sp
  INNER JOIN dbo.SalesStaff ss
  ON sp.BusinessEntityID = ss.StaffID
WHERE sp.SalesLastYear = 0;
SELECT * FROM @output;

也许这对你有所帮助。