2013-12-16 42 views
1

我想在sql server中刪除300萬條數據的數據。我正在使用一個非常簡單的查詢並批量刪除這些文件,但該命令正在執行並且仍在運行超過3個小時。造成這種低績效的可能原因是什麼?請在下面找到我正在使用的代碼刪除300萬條數據需要很多時間

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 
+3

這需要多長時間?'SELECT COUNT(*)FROM WorkflowContentDetails WHERE的startDateTime BETWEEN @StartDate和@ EndDate'這裏你可以找到一些可能的原因:http://stackoverflow.com/a/10901711/284240 –

+1

是'StartDateTime'索引?如果不是,那麼WHERE子句將在每次迭代中連續採用較長的時間,因爲它需要掃描先前讀取的非匹配行。 –

+0

你能告訴我們你在WorkflowContentDetails上的索引嗎? –

回答

1

有幾件事情,可能會導致刪除緩慢。你已經在評論中閱讀了它。

例如,您需要3M行/ 1k行= 3000次才能進行沒有idndex的搜索。更重要的是,由於缺少索引,你在表格上強加了3000次獨佔鎖定。

這是我在生產中的「刪除」模式。它解決了這些問題,即使沒有合適的索引。鎖僅在主鍵上且僅在刪除期間。

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.[ID]; 

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

-- 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每次都在執行。如果該領域沒有索引,則可能需要很長時間。取而代之的是,首先從主鍵列中選擇值(如果你有一個,並且如果有(定義在其上的)(聚集索引)),將會更好一些,然後在一個循環中工作 - 索引列上的條件它會快幾倍。

1

試試這個,爲什麼使用循環?目的?

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 
+0

如果我使用直接刪除,數據大約爲300萬,並且可能導致日誌在每個事務正在被記錄時被完全填充。 –

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/