【问题标题】:Select and Delete in the same transaction using TOP clause使用 TOP 子句在同一事务中选择和删除
【发布时间】:2012-02-23 09:10:44
【问题描述】:

我有一个表格,其中数据不断快速添加。

我需要从此表中获取记录并立即删除它们,这样我就无法第二次处理相同的记录。而且由于数据以更快的速度添加,我需要使用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 个数据。

查询在性能方面是否正常?如果我可以改进它,请提供您的建议。

谢谢

【问题讨论】:

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


【解决方案1】:

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

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 分钟,则假设业务逻辑失败”等。

【讨论】:

  • @PankajGarg - 我不确定你的确切意思。 UPDATE/OUTPUT 在隐式事务中。这意味着您更新记录并将其记录到表变量中,no 其他进程可以在那里进行干预。
  • 试想一下,假设 user1 插入 10 条记录,user2 插入 11 条记录,以此类推......这种情况会发生什么......
【解决方案2】:

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

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

Diego 的答案是为了钱——将数据放入一个变量中,然后删除匹配的行。如果可以避免的话,不要在 SQL Server 中使用锁!

【讨论】:

    【解决方案3】:

    您可以使用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】:

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

      如果是这样,您为什么不将您的第一个选择加载到临时表或变量(或至少是 PK)中,然后对您的数据做任何事情,然后根据该表进行删除?

      【讨论】:

      • 如果多个用户同时进行插入,您如何知道?
      • 有什么问题?一旦你有了 5 个 ID,就可以插入更多行,因为你不会再次选择它们
      • 感谢您的讨论。我想讨论丢失的记录 - 在多个用户同时插入期间被跳过...
      【解决方案5】:

      我知道这是个老问题,但我在这里找到了一些解决方案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;
      

      也许对你有帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-05-25
        • 1970-01-01
        • 1970-01-01
        • 2012-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多