大きなデータテーブルがあります。このテーブルには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
ENDCOMMIT TRANSACTIONとCHECKPOINTログはまだ成長しています。これを明確にしていただきありがとうございます。
                    @Deleted_Rows10000 と比較したい場合や、小さなデータセットを無期限に削除するために無限ループになる場合があることに注意してください。したがって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 100GO 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
ENDM.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()