大きなデータテーブルがあります。このテーブルには1000万のレコードがあります。
このクエリの最良の方法は何ですか
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
大きなデータテーブルがあります。このテーブルには1000万のレコードがあります。
このクエリの最良の方法は何ですか
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
回答:
そのテーブルのすべての行を削除する場合、最も簡単なオプションは次のようにテーブルをトランケートすることです
TRUNCATE TABLE LargeTable
GO
テーブルをトランケートするとテーブルが空になるだけで、WHERE句を使用して削除される行を制限することはできず、トリガーは起動されません。
一方、データの80〜90%を削除する場合、たとえば、合計1100万行あり、1千万行を削除する場合は、これらの100万行を挿入することになります(保持するレコード) )別のステージングテーブル。この大きなテーブルをトランケートし、これらの100万行を挿入します。
または、アクセス許可/ビュー、または基になるテーブルとしてこの大きなテーブルを持つ他のオブジェクトが、このテーブルをドロップしても影響を受けない場合は、これらの比較的少量の行を別のテーブルに取得して、このテーブルをドロップし、同じスキーマで別のテーブルを作成してインポートすることができます。この元の大規模なテーブルに戻る行。
私が考えることができる最後のオプションの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
回復モードを完全に戻すことを忘れないでください。完全に効果を上げるには、バックアップを取る必要があると思います(変更または回復モード)。
optimal solution for unknown case
夢である@Archont ですね。残念ながら、1つの薬ですべての病気を治すことはできません。さまざまなシナリオに対していくつかの可能な解決策を提案しました。残念ながら、ここには細長い弾丸はありません。
@ 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
COMMIT TRANSACTION
とCHECKPOINT
ログはまだ成長しています。これを明確にしていただきありがとうございます。
@Deleted_Rows
10000 と比較したい場合や、小さなデータセットを無期限に削除するために無限ループになる場合があることに注意してください。したがってWHILE (@Deleted_Rows = 10000)
、削除するデータの完全な「ページ」がなくなるとすぐに停止します。実装でWHILE (@Deleted_Rows > 0)
は、whileループは1行しか削除していなくても再度実行され、次の実行でも削除する行が1つまたは2つ見つかる可能性があり、無限ループが発生します。
WHILE
ループ自体の中で日付を繰り返し計算するため、クエリで使用される日付は反復ごとに異なりますdateadd(MONTH,-7,GETDATE())
。
WHILE
ループの異なる反復間で削除できる新しいレコードが生成される可能性があります。
GO +を使用して、同じクエリを何回実行するかを指定することもできます。
DELETE TOP (10000) [TARGETDATABASE].[SCHEMA].[TARGETTABLE]
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
GO xx
から機能するはずですか?私は取得「ストアドプロシージャが見つかりませんでした『』」エラーを。GO
コマンドがなくても問題なく動作します。
@Francisco Goldenstein、ちょっとした修正。変数を設定した後でCOMMITを使用する必要があります。そうしないと、WHILEは一度だけ実行されます。
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
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
# of rows
に削除するとWHERE
句もパラメータ化するように変更しました。魅力的な作品!
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;
SQL Server 2016以降を使用していて、テーブルに削除しようとしている列(タイムスタンプ列など)に基づいて作成されたパーティションがある場合、この新しいコマンドを使用して、パーティションごとにデータを削除できます。
(PARTITIONS({|} [、... n]))を含む表の切り捨て
これにより、選択したパーティションのデータのみが削除され、トランザクションログが作成されず、通常の切り捨てと同じ速さで実行されますが、すべてのデータが削除されないため、テーブルの一部からデータを削除する最も効率的な方法です。テーブルから。
欠点は、テーブルにパーティションが設定されていない場合、古い方法でデータを削除し、通常の方法でデータを削除してから、パーティションでテーブルを再作成して、将来これを実行できるようにする必要があります。パーティションの作成と削除を挿入手順自体に追加しました。5億行のテーブルがあったので、これが削除時間を短縮する唯一のオプションでした。
詳細については、以下のリンクを参照してください。https: //docs.microsoft.com/en-us/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()