web-dev-qa-db-ja.com

500,000行以上のテーブルでTruncate Tableを使用しないで効率的に行を削除する方法

30列500,000行のテーブルSalesがあるとします。テーブル内の400,000を削除したい("toDelete='1'")。

しかし、私はいくつかの制約があります:

  • テーブルは「頻繁に」読み書きされるので、長い「削除」に時間がかかり、テーブルをあまりにも長くロックしたくない
  • トランザクションログをスキップする必要があります(TRUNCATEなど)。ただし、"DELETE ... WHERE..."(条件を設定する必要がありますが、これを行う方法が見つかりませんでした...

どんなアドバイスも歓迎します

DELETE FROM Sales WHERE toDelete='1'

よりパーティション化された、おそらくトランザクションログのないものに。

27
Skippy Fastol

DELETE FROM TableNameを呼び出すと、1つの大きなトランザクションで削除全体が実行されます。これは高価です。

バッチで行を削除する別のオプションを次に示します。

deleteMore:
DELETE TOP(10000) Sales WHERE toDelete='1'
IF @@ROWCOUNT != 0
    goto deleteMore
35
Kevin Aenmey

必要なのはバッチ処理です。

While (select Count(*) from sales where toDelete =1) >0
BEGIN
Delete from sales where SalesID in
(select top 1000 salesId from sales where toDelete = 1)
END

もちろん、バッチに使用するのに最適な値を試すことができます。テーブルに応じて500〜50000を使用しました。カスケード削除を使用する場合、削除する子レコードがあるため、おそらくより小さい番号が必要になります。

11
HLGEM

過去にこれを行わなければならなかった1つの方法は、nレコードを削除するストアドプロシージャまたはスクリプトを作成することです。完了するまで繰り返します。

DELETE TOP 1000 FROM Sales WHERE toDelete='1'
5
Cylindric

この機能に関する私自身の見解は次のとおりです。この方法では、繰り返されるコードはなく、チャンクサイズを管理できます。

DECLARE @DeleteChunk INT = 10000
DECLARE @rowcount INT = 1

WHILE @rowcount > 0
BEGIN

  DELETE TOP (@DeleteChunk) FROM Sales WITH(ROWLOCK)

  SELECT @rowcount = @@RowCount
END
3
WaitForPete

テーブル全体をロックしないように、ROWLOCKヒントを与えるようにしてください。ただし、多くの行を削除すると、ロックのエスカレーションが発生します。

また、toDelete列にクラスター化されていないfilteredインデックス(1つの値のみ)があることを確認してください。可能であれば、varchar(または現在の状態)ではなく、ビット列にします。

DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'

最終的に、テーブルを反復処理して、チャンク単位で削除を試みることができます。

更新済み

ここではループとチャンクの削除が新しいピンクなので、私のバージョンもスローします(以前の回答と組み合わせて):

SET ROWCOUNT 100
DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'

WHILE @@rowcount > 0
BEGIN
  SET ROWCOUNT 100
  DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1'  
END
3
Marcel N.

大量の削除と更新のさまざまなアプローチをテストできたので、ここに答えを残します(125 + mio行を更新してから削除する必要がありました、サーバーには16GBのRAM、Xeon E5-2680 @ 2.7GHz、SQL Server 2012)。

TL; DR:常に主キーで更新/削除します。他の条件では更新しません。 PKを直接使用できない場合は、一時テーブルを作成してPK値を入力し、そのテーブルを使用してテーブルを更新/削除します。これにはインデックスを使用します。

上記 (@Kevin Aenmeyによる)から解決策を開始しましたが、このアプローチは不適切であることが判明しました。データベースが稼働しており、1秒間に数百のトランザクションを処理し、いくつかのブロッキングが含まれていた(WITH(ROWLOCK)を使用して条件からのすべてのそこにフィールドのインデックスがありましたが、何も変更しませんでした)。

そこで、 WAITFOR ステートメントを追加しました。これにより、データベースが他のトランザクションを処理できるようになりました。

deleteMore:
WAITFOR DELAY '00:00:01'
DELETE TOP(1000) FROM MyTable WHERE Column1 = @Criteria1 AND Column2 = @Criteria2 AND Column3 = @Criteria3
IF @@ROWCOUNT != 0
    goto deleteMore

このアプローチでは、更新に〜1.6mio行/時間、削除に〜0,2mio行/時間を処理できました。

一時テーブルに目を向けると、状況は大きく変わりました。

deleteMore:
SELECT TOP 10000 Id /* Id is the PK */
  INTO #Temp 
  FROM MyTable WHERE Column1 = @Criteria1 AND Column2 = @Criteria2 AND Column3 = @Criteria3 

DELETE MT
  FROM MyTable MT
  JOIN #Temp T ON T.Id = MT.Id 

/* you can use IN operator, it doesn't change anything
 DELETE FROM MyTable WHERE Id IN (SELECT Id FROM #Temp)

 */
IF @@ROWCOUNT > 0 BEGIN
    DROP TABLE #Temp
    WAITFOR DELAY '00:00:01'
    goto deleteMore
END ELSE BEGIN
    DROP TABLE #Temp
    PRINT 'This is the end, my friend'
END

このソリューションは、更新で約25mio行/時間(15倍高速)、削除で約2.2mio行/時間(11倍高速)を処理しました。

2

以下を使用して約5,000万件のレコードを削除しました-

BEGIN TRANSACTION     
     DeleteOperation:
     DELETE TOP (BatchSize)
     FROM  [database_name].[database_schema].[database_table] 

     IF @@ROWCOUNT > 0
     GOTO DeleteOperation
COMMIT TRANSACTION

BatchSize <5000を維持すると、リソースのコストが低くなることに注意してください。

1
Ankush

大量のレコードを削除する最善の方法は、Primary Key。 (とは Primary Keyこちらを参照

したがって、削除する行のリスト全体を含むtsqlスクリプトを生成し、このスクリプトを実行した後に実行する必要があります。

以下の例のコードはそのファイルを生成するつもりです

GO
SET NOCOUNT ON

SELECT   'DELETE FROM  DATA_ACTION WHERE ID = ' + CAST(ID AS VARCHAR(50)) + ';' + CHAR(13) + CHAR(10) + 'GO'
FROM    DATA_ACTION
WHERE  YEAR(AtTime) = 2014

出力ファイルには次のようなレコードがあります

DELETE FROM  DATA_ACTION WHERE ID = 123;
GO
DELETE FROM  DATA_ACTION WHERE ID = 124;
GO
DELETE FROM  DATA_ACTION WHERE ID = 125;
GO

このスクリプトを実行するには、SQLCMDユーティリティを使用する必要があります。

sqlcmd -S [Instance Name] -E -d [Database] -i [Script]

このアプローチはここで説明されています https://www.mssqltips.com/sqlservertip/3566/deleting-historical-data-from-a-large-highly-concurrent-sql-server-database-table/

0
Developer