大きなデータテーブルがあります。このテーブルには1000万レコードがあります。
このクエリに最適な方法は何ですか
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
そのテーブルのすべての行を削除する場合、最も簡単なオプションはテーブルの切り捨てです。
TRUNCATE TABLE LargeTable
GO
切り捨てテーブルは単にテーブルを空にするでしょう、あなたは削除される行を制限するためにWHERE句を使用することができず、トリガーは起動されません。
一方、80〜90パーセントを超えるデータを削除する場合、合計1,100万行あり、別の方法で1000万行を削除する場合は、これらの100万行を挿入することになります。 )別のステージングテーブルに移動します。この大きなテーブルを切り捨てて、これらの100万行を挿入します。
あるいは、この大きなテーブルを基になるテーブルとして持つパーミッション/ビューまたは他のオブジェクトがこのテーブルを削除しても影響を受けない場合は、これらの比較的少量の行を別のテーブルにドロップして同じテーブルを作成してインポートこのex-Largeテーブルに行を戻します。
私が考えることができる最後のオプションの1つは、データベースのRecovery Mode to SIMPLE
を変更してから、whileループを使用して小さいバッチで行を削除することです。
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
END
回復モードを完全に戻すことを忘れないでください。完全に有効にするにはバックアップを取る必要があると思います(変更モードまたは回復モード)。
@ m-ALiの答えは正しいですが、チャンクごとにトランザクションをコミットしてチェックポイントを実行しないと、ログが大きくなる可能性があることにも注意してください。これが私のやり方であり、この記事を参考にしてください http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes 、パフォーマンステストとグラフを参考にしてください。
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
GO +を使用して、同じクエリを何回実行することもできます。
DELETE TOP (10000) [TARGETDATABASE].[SCHEMA].[TARGETTABLE]
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
M.ALiのこのバリエーションは私にとってはうまく機能しています。一部を削除し、ログを消去して繰り返します。私はログが大きくなり、ドロップしてやり直すのを見ています。
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
delete top (100000) from InstallLog where DateTime between '2014-12-01' and '2015-02-01'
SET @Deleted_Rows = @@ROWCOUNT;
dbcc shrinkfile (MobiControlDB_log,0,truncateonly);
END
@Francisco Goldenstein、ちょっとした訂正。変数を設定した後はCOMMITを使用する必要があります。そうしないと、WHILEが1回だけ実行されます。
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
パーティショニングを実装する意思がある(そして可能である)場合は、ランタイムオーバーヘッドをほとんど発生させずに大量のデータを削除するのに効果的な手法です。ただし、一回限りの運動では費用対効果は高くありません。
2100万行のテーブルから1900万行を数分で削除できました。これが私のアプローチです。
このテーブルに自動インクリメントの主キーがある場合は、この主キーを使用できます。
ReadTime <dateadd(MONTH、-7、GETDATE())の場合、ラージテーブルの主キーの最小値を取得します。 (readTimeにインデックスを追加します。まだ存在していない場合は、このインデックスはとにかくステップ3のテーブルと共に削除されます)。変数 'min_primary'に保存しましょう。
主キー> min_primaryを持つすべての行をステージング表(行数が大きくない場合はメモリー表)に挿入します。
大きなテーブルを削除します。
テーブルを再作成してください。ステージングテーブルからメインテーブルにすべての行をコピーします。
ステージングテーブルを削除します。
Whileループを使って小さなバッチを削除することができます。
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
もう一つの用途:
SET ROWCOUNT 1000 -- Buffer
DECLARE @DATE AS DATETIME = dateadd(MONTH,-7,GETDATE())
DELETE LargeTable WHERE readTime < @DATE
WHILE @@ROWCOUNT > 0
BEGIN
DELETE LargeTable WHERE readTime < @DATE
END
SET ROWCOUNT 0
オプションです。
トランザクションログが有効になっている場合は、トランザクションログを無効にします。
ALTER DATABASE dbname SET RECOVERY SIMPLE;
短い構文
select 1
WHILE (@@ROWCOUNT > 0)
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
SQL Server 2016以上を使用していて、テーブルに削除しようとしている列に基づいてパーティションが作成されている場合(タイムスタンプ列など)、この新しいコマンドを使用してパーティションごとにデータを削除できます。
(PARTITIONS({|} [、... n]))で表を切り捨てます
これは選択したパーティション内のデータのみを削除し、トランザクションログを作成せず、通常の切り捨てと同じくらいの速さでデータを削除せずにテーブルの一部からデータを削除する最も効率的な方法です。テーブルから。
欠点は、テーブルにパーティションが設定されていない場合は、通常の方法で古い学校に行ってデータを削除してから、パーティションを作成してテーブルを再作成する必要があることです。挿入手順自体にパーティションの作成と削除を追加しました。私は5億行のテーブルを持っていたので、これが削除時間を減らす唯一の選択肢でした。
詳細については、以下のリンクを参照してください。 https://docs.Microsoft.com/ja-jp/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
SQL Server 2016パーティション付きテーブル切り捨て
以下に、必要なデータが入っているパーティションを含むテーブルを再作成する前に、最初にデータを削除したものを示します。このクエリは、データが削除されるまで、指定された期間内に数日間実行されます。
:connect <<ServerName>>
use <<DatabaseName>>
SET NOCOUNT ON;
DECLARE @Deleted_Rows INT;
DECLARE @loopnum INT;
DECLARE @msg varchar(100);
DECLARE @FlagDate datetime;
SET @FlagDate = getdate() - 31;
SET @Deleted_Rows = 1;
SET @loopnum = 1;
/*while (getdate() < convert(datetime,'2018-11-08 14:00:00.000',120))
BEGIN
RAISERROR( 'WAIT for START' ,0,1) WITH NOWAIT
WAITFOR DELAY '00:10:00'
END*/
RAISERROR( 'STARTING PURGE' ,0,1) WITH NOWAIT
WHILE (1=1)
BEGIN
WHILE (@Deleted_Rows > 0 AND (datepart(hh, getdate() ) >= 12 AND datepart(hh, getdate() ) <= 20)) -- (getdate() < convert(datetime,'2018-11-08 19:00:00.000',120) )
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (500000) dbo.<<table_name>>
WHERE timestamp_column < convert(datetime, @FlagDate,102)
SET @Deleted_Rows = @@ROWCOUNT;
WAITFOR DELAY '00:00:01'
select @msg = 'ROWCOUNT' + convert(varchar,@Deleted_Rows);
set @loopnum = @loopnum + 1
if @loopnum > 1000
begin
begin try
DBCC SHRINKFILE (N'<<databasename>>_log' , 0, TRUNCATEONLY)
RAISERROR( @msg ,0,1) WITH NOWAIT
end try
begin catch
RAISERROR( 'DBCC SHRINK' ,0,1) WITH NOWAIT
end catch
set @loopnum = 1
end
END
WAITFOR DELAY '00:10:00'
END
select getdate()