Sql server 删除表中的行会导致锁定

Sql server 删除表中的行会导致锁定,sql-server,sql-server-2008,tsql,Sql Server,Sql Server 2008,Tsql,我正在运行以下命令从一个大表(1.5亿行)中批量删除行: DECLARE@RowCount int 而1=1 开始 删除顶部(10000)t1 来自表t1 t2上的内部联接表2 t2.PrimaryKey=t1.PrimaryKey 我想你的思路是对的 再看看这两篇文章: 以及: 在运行删除之前,请检查估计的查询计划,看看它是否正确 正在为删除执行索引查找,还是仍在执行完整表 扫描/访问 首先-看起来您的DELETE正在执行聚集索引扫描,我建议您执行以下操作: create index

我正在运行以下命令从一个大表(1.5亿行)中批量删除行:

DECLARE@RowCount int
而1=1
开始
删除顶部(10000)t1
来自表t1
t2上的内部联接表2 t2.PrimaryKey=t1.PrimaryKey

我想你的思路是对的

再看看这两篇文章:

以及:

在运行删除之前,请检查估计的查询计划,看看它是否正确 正在为删除执行索引查找,还是仍在执行完整表 扫描/访问


首先-看起来您的DELETE正在执行聚集索引扫描,我建议您执行以下操作:

create index [IX.IndexName] ON t1(YearProcessed, PrimaryKey)
第二,是否需要加入t2表

然后使用以下查询删除行,假设PrimaryKey列的类型为INT:

declare @ids TABLE(PrimaryKey INT)
WHILE 1=1
    BEGIN
        INSERT @ids 
        SELECT top 10000 DISTINCT t1.PrimaryKey
        FROM table t1
        INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey
        WHERE t1.YearProcessed <= 2007

        IF @@ROWCOUNT = 0 BREAK

        DELETE  t1
        WHERE PrimaryKey in (Select PrimaryKey from @ids)

        delete from @ids

    END
declare@ids表(PrimaryKey INT)
而1=1
开始
插入@ids
选择top 10000 DISTINCT t1.PrimaryKey
来自表t1
t2上的内部联接表2 t2.PrimaryKey=t1.PrimaryKey

其中t1.YearProcessed除了其他建议(旨在减少删除过程中所做的工作)之外,还可以将SQL Server配置为在表上执行删除操作时不阻止其他读卡器

这可以通过使用SQL Server 2005引入的“快照隔离”来实现:


我过去也见过类似的零星问题,即使在5000条记录的小批量中,锁定仍然会发生。在我们的例子中,每个删除/更新都包含在自己的Begin Tran…Commit循环中。要纠正这个问题,需要

等待延迟“00:00:00:01”


已放置在每个循环的顶部,从而更正了问题。

如果您有任何级联删除,请确保它们已被索引

突出显示删除查询并单击“显示估计的执行计划”
将显示建议的索引,在我的例子中,这些索引包括一些级联删除


为这些行添加索引使删除速度大大加快,但我仍然不会尝试一次删除所有行。

我找到的最佳方法是表单asp.net DeleteExpiredSessions。执行READUNCOMMITED select并将记录放入临时表中,然后使用光标删除记录

 ALTER PROCEDURE [dbo].[DeleteExpiredSessions]
    AS
        SET NOCOUNT ON
        SET DEADLOCK_PRIORITY LOW 

        DECLARE @now datetime
        SET @now = GETUTCDATE() 

        CREATE TABLE #tblExpiredSessions 
        ( 
            SessionID nvarchar(88) NOT NULL PRIMARY KEY
        )

        INSERT #tblExpiredSessions (SessionID)
            SELECT SessionID
            FROM [ASPState].dbo.ASPStateTempSessions WITH (READUNCOMMITTED)
            WHERE Expires < @now

        IF @@ROWCOUNT <> 0 
        BEGIN 
            DECLARE ExpiredSessionCursor CURSOR LOCAL FORWARD_ONLY READ_ONLY
            FOR SELECT SessionID FROM #tblExpiredSessions 

            DECLARE @SessionID nvarchar(88)

            OPEN ExpiredSessionCursor

            FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID

            WHILE @@FETCH_STATUS = 0 
                BEGIN
                    DELETE FROM [ASPState].dbo.ASPStateTempSessions WHERE SessionID = @SessionID AND Expires < @now
                    FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID
                END

            CLOSE ExpiredSessionCursor

            DEALLOCATE ExpiredSessionCursor

        END 

        DROP TABLE #tblExpiredSessions

    RETURN 0   
ALTER过程[dbo]。[DeleteExpiredSessions]
作为
不计较
将死锁设置为低优先级
声明@now datetime
SET@now=GETUTCDATE()
创建表#tblExpiredSessions
( 
SessionID nvarchar(88)主键不为空
)
插入#tblExpiredSessions(SessionID)
选择SessionID
来自[ASPState].dbo.ASPStateTempSessions与(READUNCOMMITTED)
现在在哪里
如果@@ROWCOUNT 0
开始
声明ExpiredSessionCursor游标本地转发只读
对于从#tblExpiredSessions中选择SessionID
声明@SessionID nvarchar(88)
打开过期的sessioncursor
从ExpiredSessionCursor获取下一个到@SessionID
而@@FETCH\u STATUS=0
开始
从[ASPState].dbo.ASPStateTempSessions中删除,其中SessionID=@SessionID并立即过期
从ExpiredSessionCursor获取下一个到@SessionID
结束
关闭过期的sessioncursor
取消分配过期的SessionCursor
结束
DROP TABLE#tblExpiredSessions
返回0
试试这个

DECLARE @RowCount int
WHILE 1=1
    BEGIN
        BEGIN TRANSACTION 
        DELETE TOP (10000) t1
        FROM table t1
        INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey
        WHERE t1.YearProcessed <= 2007
         END TRANSACTION 
         COMMIT TRANSACTION 
        SET @RowCount = @@ROWCOUNT

        IF (@RowCount < 10000) BREAK
    END
DECLARE@RowCount int
而1=1
开始
开始交易
删除顶部(10000)t1
来自表t1
t2上的内部联接表2 t2.PrimaryKey=t1.PrimaryKey

其中t1.YearProcessed实际上是从父表内部连接要删除的主键列表(实际上在表变量中)。我刚刚发布了两个表来简化脚本编写。与您所做的非常相似,但您采用了不同的方法,我将尝试这种方法。值得注意的是,快照隔离需要更改单个事务的隔离级别。读取提交的快照隔离将更改默认隔离级别,但它需要一个非常短暂的中断。这是在每个批处理或每行删除的开始/结束时进行的?
DECLARE @RowCount int
WHILE 1=1
    BEGIN
        BEGIN TRANSACTION 
        DELETE TOP (10000) t1
        FROM table t1
        INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey
        WHERE t1.YearProcessed <= 2007
         END TRANSACTION 
         COMMIT TRANSACTION 
        SET @RowCount = @@ROWCOUNT

        IF (@RowCount < 10000) BREAK
    END