おーみんブログ

C#, ASP.NET Core, Unityが大好きです。

【C#】大量データを削除する際のひと工夫。

はじめに

現在参画している案件にて、お客様先(常駐している現場)の上司に大量データの処理の方法を教えてもらったので備忘録として残しておこうと思います。

大量のデータの処理で落ちる

データベースにたまった不要な大量データを削除するとしましょう。 そのデータが数件程度であればそのままDelete文を書いて削除してしまえばいいのですが、数十万件あるデータだとなかなか一筋縄にはいきません。 例えばEntityFramework Coreを用いて特定のレコードを削除する場合、素直に書くと以下のようになるかと思います。

var selecedDeleteTargets = await _dbContext.BigTable
    .Where(o => o.Name == "削除対象")
    .ToListAsync();

_dbContext.BigTable.RemoveRange(selectedDeleteTargets);
await _dbContext.SaveChangesAsync();

しかしながら対象のレコードが大量にある場合、ToListAsync()をした時点で落ちてしまいます(out of memory)。

LINQのTake用いて処理を工夫する

そこで処理を以下のように工夫してみます。 一度に取ってダメなら小分けにして削除していくぞ!という方針です。

while(true)
{
    var selectedDeleteTargets = await _dbContext.BigTable
        .Where(o => o.Name == "削除対象")
        .Take(1000)
        .ToListAsync();

    if(!selectedDeleteTargets.Any())
        break;

    using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
    _dbContext.BigTable.RemoveRange(selectedDeleteTargets);
    await _dbContext.SaveChangesAsync();
    transaction.Complete();
}

上記のコードは削除対象のレコードが残っている限り1000件ずつ取得して削除していくという処理になります。 (ちなみに削除対象のレコードが残り500件しかないという場合でもTake(1000)は対象の分だけ取得してくるので大丈夫です。)

上記のようにTransactionScopeを用いてトランザクションも張っておくとより良さそうです。

おわりに

以上、大量データを削除する際のひと工夫でした。 もし他にも良い方法などがありましたらぜひぜひ教えてください~!