删除300万个数据需要花费大量时间

问题描述 投票:1回答:4

我想在sql server中删除基于数据的300万个数据。我正在使用一个非常简单的查询和批量删除文件但该命令正在执行并且仍然运行超过3个小时。这种低性能的可能原因可能是什么。请找到我正在使用的下面的代码

Create PROC [dbo].[DeleteOneWeekOldData] 
@StartDate DateTime,
@EndDate DateTime

AS

DECLARE @continue INT
DECLARE @rowcount INT

SET @continue = 1
WHILE @continue = 1
BEGIN
    SET ROWCOUNT 1000
    BEGIN TRANSACTION
    DELETE FROM WorkflowContentDetails WHERE  StartDateTime BETWEEN @StartDate AND @EndDate
    SET @rowcount = @@rowcount 
    COMMIT
       IF @rowcount = 0
    BEGIN
        SET @continue = 0
    END
END

GO
sql-server tsql sql-delete large-data
4个回答
2
投票

一些可能导致删除速度缓慢的事情。您已在评论中阅读过相关内容。

对于前者你需要3M行/ 1k行= 3000次来搜索没有索引。更重要的是,由于缺少索引,你在桌子上强加了3000次锁定。

这是我生产中的“DELETE”模式。它解决了这些问题,即使没有合适的索引。锁仅在主键上,仅在删除期间。

SET NOCOUNT ON

DECLARE 
    @StartDate DateTime = '20130101',
    @EndDate DateTime = '20131201'

DECLARE @chunk_size bigint = 1000
DECLARE @RowCount bigint;
DECLARE @delay      DATETIME = '00:00:01'       --- 1 second by defaul, Used for delaying the updates inside the loop, can be 0

DECLARE @Chunk_IDs as TABLE (
    [ID] int NOT NULL,
    [SourcePKID] int not null
);

IF OBJECT_ID('tempdb..#temp_OldDataIDs') is not null
    DROP TABLE #temp_OldDataIDs;

CREATE TABLE #temp_OldDataIDs (
    [ID]            INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    [SourcePKID]    INT NOT NULL,
    [DeletedStatus] BIT NOT NULL DEFAULT 0);

INSERT INTO #temp_OldDataIDs ([SourcePKID])
SELECT [ID]
FROM WorkflowContentDetails 
WHERE StartDateTime BETWEEN @StartDate AND @EndDate

SET @RowCount = @@ROWCOUNT;

CREATE NONCLUSTERED INDEX IX_#temp_OldDataIDs on #temp_OldDataIDs ([DeletedStatus]) include([ID],SourcePKID)

WHILE (@RowCount != 0)
BEGIN

    DELETE @Chunk_IDs;

    INSERT INTO @Chunk_IDs ([ID], [SourcePKID])
    SELECT TOP (@chunk_size)
        [ID], [SourcePKID]
    FROM #temp_OldDataIDs
    WHERE [DeletedStatus] = 0
    ORDER BY [ID];

    SET @RowCount = @@ROWCOUNT;
    IF @RowCount = 0 BREAK;


    DELETE WorkflowContentDetails
    FROM WorkflowContentDetails
        INNER JOIN @Chunk_IDs ChunkIDs ON WorkflowContentDetails.[ID] = ChunkIDs.[SourcePKID];

    UPDATE OldIDs
        SET [DeletedStatus] = 1
    FROM #temp_OldDataIDs OldIDs
        INNER JOIN @Chunk_IDs ChunkIDs ON OldIDs.[ID] = ChunkIDs.[SourcePKID];

-- debug
-- PRINT CAST(@RowCount as varchar(30)) + ' ' + CONVERT(varchar(30), GETDATE(),121)

    -- The requested delay will hold the loop here as requested.
    WAITFOR DELAY  @delay
END


GO

3
投票

您的查询有两个问题。

  1. SET ROWCOUNT 1000导致数据以非常小的块被删除。如果需要删除1M行,那将是1000次删除操作。与大量小型操作相比,SQL Server将更好地处理少量大型操作。此外,SET ROWCOUNT将不再影响未来版本中的插入/更新/删除操作。
  2. StartDateTime BETWEEN @StartDate AND @EndDate每次都在执行。如果该字段上没有索引,则可能需要很长时间。而不是那样,最好首先从主键列中选择值(如果你有一个,如果有(聚集的)索引定义在它上面)到临时表中然后在循环中工作 - 条件为索引列它的工作速度会快几倍。

3
投票

在这种情况下,您的最佳性能是按日期对表进行分区,然后在不再需要时截断或删除分区(而不是使用DELETE语句)。

性能提升的原因是:

1)通过指定要截断的分区,根据定义,您还定义了日期范围,因此SQL Server无需搜索它,无论是使用基于索引的检索还是表扫描,其中任何一个都需要时间。

2)TRUNCATE操作是DDL,而不是像DELETE这样的DML,因此操作不会写入事务日志,使其更快。它也没有填充日志文件的风险。 (当然,如果您使用其中任何一种,这可能会对增量备份和复制产生影响。)

在过去,我看到TRUNCATE操作在不到一分钟的时间内完成,相应的DELETE需要几个小时。

这里有一个战略的演练:http://www.galaxysql.com/2013/09/sql-server-partitioning-for-performance-and-archiving/


1
投票

试试这个,为什么r使用循环?目的?

Create PROC [dbo].[DeleteOneWeekOldData] 
@StartDate DateTime,
@EndDate DateTime

AS
begin
Set NoCount on
DECLARE @continue INT
DECLARE @rowcount INT
Declare @err int
--SET @continue = 1 --why ar u using these commented part
--WHILE @continue = 1
--BEGIN
--    SET ROWCOUNT 1000
    BEGIN TRANSACTION
    DELETE FROM WorkflowContentDetails WHERE  StartDateTime BETWEEN @StartDate AND @EndDate
    SET @err = @@Error
    if(@err<>0)
    COMMIT
      else
       rollback
    END

GO
© www.soinside.com 2019 - 2024. All rights reserved.